# Extra modules in Jeeps that we don't use
# jeeps/gpsfmt.o jeeps/gpsinput.o jeeps/gpsproj.o
-SHAPE=shapelib/shpopen.o shapelib/dbfopen.o
+SHAPE=shapelib/shpopen.o shapelib/dbfopen.o shapelib/safileio.o
ZLIB=zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/deflate.o zlib/inffast.o \
zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/trees.o \
-This is a subset of Shapelib v1.2.10 from http://shapelib.maptools.org/
+This is a subset of Shapelib v1.3.10 from http://shapelib.maptools.org/
-The source is unmodified. It's subsetted here only to reduce the amount of
-size in our tree that it takes and to reduce ongoing merge maintenance.
+The source is largely unmodified. It's subsetted here only to reduce
+the amount of size in our tree that it takes and to reduce ongoing
+merge maintenance.
+
+shpopen.c: int32_t instead of int32 was used to eliminate clang warnings.
<!-------------------------------------------------------------------------->
+<h2>DBFIsRecordDeleted()</h2>
+
+<pre>
+int DBFIsRecordDeleted( DBFHandle hDBF, int iShape );
+
+ hDBF: The access handle for the file to be checked.
+ iShape: The record index to check.
+</pre>
+
+ Returns TRUE (non-zero) if the record is marked for deletion, otherwise
+ it returns FALSE.<p>
+
+<!-------------------------------------------------------------------------->
+
+<h2>DBFMarkRecordDeleted()</h2>
+
+<pre>
+int DBFMarkRecordDeleted( DBFHandle hDBF, int iShape, int bIsDeleted );
+
+ hDBF: The access handle for the file.
+ iShape: The record index to update.
+ bIsDeleted: TRUE to mark record deleted, or FALSE to undelete it.
+</pre>
+
+ Returns TRUE on success, or FALSE on error.<p>
+
+<!-------------------------------------------------------------------------->
+
<h2>DBFGetNativeFieldType()</h2>
<pre>
/******************************************************************************
- * $Id: dbfopen.c,v 1.3 2006-07-13 03:27:54 robertl Exp $
+ * $Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $
*
* Project: Shapelib
* Purpose: Implementation of .dbf access API documented in dbf_api.html.
* DEALINGS IN THE SOFTWARE.
******************************************************************************
*
- * $Log: not supported by cvs2svn $
- * Revision 1.2 2006/05/07 02:14:35 robertl
- * Make shapefile and all palm pdb formats deselectable at build time.
+ * $Log: dbfopen.c,v $
+ * Revision 1.89 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
*
- * Revision 1.1 2004/09/20 17:21:22 robertl
- * Check in shapelib and experimental prototype of crude shapefile support.
+ * Revision 1.88 2011-05-13 17:35:17 fwarmerdam
+ * added DBFReorderFields() and DBFAlterFields() functions (from Even)
*
- * Revision 1.48 2003/03/10 14:51:27 warmerda
- * DBFWrite* calls now return FALSE if they have to truncate
+ * Revision 1.87 2011-05-07 22:41:02 fwarmerdam
+ * ensure pending record is flushed when adding a native field (GDAL #4073)
*
- * Revision 1.47 2002/11/20 03:32:22 warmerda
- * Ensure field name in DBFGetFieldIndex() is properly terminated.
+ * Revision 1.86 2011-04-17 15:15:29 fwarmerdam
+ * Removed unused variable.
*
- * Revision 1.46 2002/10/09 13:10:21 warmerda
- * Added check that width is positive.
+ * Revision 1.85 2010-12-06 16:09:34 fwarmerdam
+ * fix buffer read overrun fetching code page (bug 2276)
*
- * Revision 1.45 2002/09/29 00:00:08 warmerda
- * added FTLogical and logical attribute read/write calls
+ * Revision 1.84 2009-10-29 19:59:48 fwarmerdam
+ * avoid crash on truncated header (gdal #3093)
*
- * Revision 1.44 2002/05/07 13:46:11 warmerda
- * Added DBFWriteAttributeDirectly().
+ * Revision 1.83 2008/11/12 14:28:15 fwarmerdam
+ * DBFCreateField() now works on files with records
*
- * Revision 1.43 2002/02/13 19:39:21 warmerda
- * Fix casting issues in DBFCloneEmpty().
+ * Revision 1.82 2008/11/11 17:47:09 fwarmerdam
+ * added DBFDeleteField() function
*
- * Revision 1.42 2002/01/15 14:36:07 warmerda
- * updated email address
+ * Revision 1.81 2008/01/03 17:48:13 bram
+ * in DBFCreate, use default code page LDID/87 (= 0x57, ANSI)
+ * instead of LDID/3. This seems to be the same as what ESRI
+ * would be doing by default.
*
- * Revision 1.41 2002/01/15 14:31:49 warmerda
- * compute rather than copying nHeaderLength in DBFCloneEmpty()
+ * Revision 1.80 2007/12/30 14:36:39 fwarmerdam
+ * avoid syntax issue with last comment.
*
- * Revision 1.40 2002/01/09 04:32:35 warmerda
- * fixed to read correct amount of header
+ * Revision 1.79 2007/12/30 14:35:48 fwarmerdam
+ * Avoid char* / unsigned char* warnings.
*
- * Revision 1.39 2001/12/11 22:41:03 warmerda
- * improve io related error checking when reading header
+ * Revision 1.78 2007/12/18 18:28:07 bram
+ * - create hook for client specific atof (bugzilla ticket 1615)
+ * - check for NULL handle before closing cpCPG file, and close after reading.
*
- * Revision 1.38 2001/11/28 16:07:31 warmerda
- * Cleanup to avoid compiler warnings as suggested by Richard Hash.
+ * Revision 1.77 2007/12/15 20:25:21 bram
+ * dbfopen.c now reads the Code Page information from the DBF file, and exports
+ * this information as a string through the DBFGetCodePage function. This is
+ * either the number from the LDID header field ("LDID/<number>") or as the
+ * content of an accompanying .CPG file. When creating a DBF file, the code can
+ * be set using DBFCreateEx.
*
- * Revision 1.37 2001/07/04 05:18:09 warmerda
- * do last fix properly
+ * Revision 1.76 2007/12/12 22:21:32 bram
+ * DBFClose: check for NULL psDBF handle before trying to close it.
*
- * Revision 1.36 2001/07/04 05:16:09 warmerda
- * fixed fieldname comparison in DBFGetFieldIndex
+ * Revision 1.75 2007/12/06 13:58:19 fwarmerdam
+ * make sure file offset calculations are done in as SAOffset
*
- * Revision 1.35 2001/06/22 02:10:06 warmerda
- * fixed NULL shape support with help from Jim Matthews
+ * Revision 1.74 2007/12/06 07:00:25 fwarmerdam
+ * dbfopen now using SAHooks for fileio
*
- * Revision 1.33 2001/05/31 19:20:13 warmerda
- * added DBFGetFieldIndex()
+ * Revision 1.73 2007/09/03 19:48:11 fwarmerdam
+ * move DBFReadAttribute() static dDoubleField into dbfinfo
*
- * Revision 1.32 2001/05/31 18:15:40 warmerda
- * Added support for NULL fields in DBF files
+ * Revision 1.72 2007/09/03 19:34:06 fwarmerdam
+ * Avoid use of static tuple buffer in DBFReadTuple()
*
- * Revision 1.31 2001/05/23 13:36:52 warmerda
- * added use of SHPAPI_CALL
+ * Revision 1.71 2006/06/22 14:37:18 fwarmerdam
+ * avoid memory leak if dbfopen fread fails
*
- * Revision 1.30 2000/12/05 14:43:38 warmerda
- * DBReadAttribute() white space trimming bug fix
+ * Revision 1.70 2006/06/17 17:47:05 fwarmerdam
+ * use calloc() for dbfinfo in DBFCreate
*
- * Revision 1.29 2000/10/05 14:36:44 warmerda
- * fix bug with writing very wide numeric fields
+ * Revision 1.69 2006/06/17 15:34:32 fwarmerdam
+ * disallow creating fields wider than 255
*
- * Revision 1.28 2000/09/25 14:18:07 warmerda
- * Added some casts of strlen() return result to fix warnings on some
- * systems, as submitted by Daniel.
+ * Revision 1.68 2006/06/17 15:12:40 fwarmerdam
+ * Fixed C++ style comments.
*
- * Revision 1.27 2000/09/25 14:15:51 warmerda
- * added DBFGetNativeFieldType()
+ * Revision 1.67 2006/06/17 00:24:53 fwarmerdam
+ * Don't treat non-zero decimals values as high order byte for length
+ * for strings. It causes serious corruption for some files.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=1202
*
- * Revision 1.26 2000/07/07 13:39:45 warmerda
- * removed unused variables, and added system include files
+ * Revision 1.66 2006/03/29 18:26:20 fwarmerdam
+ * fixed bug with size of pachfieldtype in dbfcloneempty
*
- * Revision 1.25 2000/05/29 18:19:13 warmerda
- * avoid use of uchar, and adding casting fix
+ * Revision 1.65 2006/02/15 01:14:30 fwarmerdam
+ * added DBFAddNativeFieldType
*
- * Revision 1.24 2000/05/23 13:38:27 warmerda
- * Added error checks on return results of fread() and fseek().
+ * Revision 1.64 2006/02/09 00:29:04 fwarmerdam
+ * Changed to put spaces into string fields that are NULL as
+ * per http://bugzilla.maptools.org/show_bug.cgi?id=316.
*
- * Revision 1.23 2000/05/23 13:25:49 warmerda
- * Avoid crashing if field or record are out of range in dbfread*attribute().
+ * Revision 1.63 2006/01/25 15:35:43 fwarmerdam
+ * check success on DBFFlushRecord
*
- * Revision 1.22 1999/12/15 13:47:24 warmerda
- * Added stdlib.h to ensure that atof() is prototyped.
+ * Revision 1.62 2006/01/10 16:28:03 fwarmerdam
+ * Fixed typo in CPLError.
*
- * Revision 1.21 1999/12/13 17:25:46 warmerda
- * Added support for upper case .DBF extention.
+ * Revision 1.61 2006/01/10 16:26:29 fwarmerdam
+ * Push loading record buffer into DBFLoadRecord.
+ * Implement CPL error reporting if USE_CPL defined.
*
- * Revision 1.20 1999/11/30 16:32:11 warmerda
- * Use atof() instead of sscanf().
+ * Revision 1.60 2006/01/05 01:27:27 fwarmerdam
+ * added dbf deletion mark/fetch
*
- * Revision 1.19 1999/11/05 14:12:04 warmerda
- * updated license terms
+ * Revision 1.59 2005/03/14 15:20:28 fwarmerdam
+ * Fixed last change.
*
- * Revision 1.18 1999/07/27 00:53:28 warmerda
- * ensure that whole old field value clear on write of string
+ * Revision 1.58 2005/03/14 15:18:54 fwarmerdam
+ * Treat very wide fields with no decimals as double. This is
+ * more than 32bit integer fields.
*
- * Revision 1.1 1999/07/05 18:58:07 warmerda
- * New
+ * Revision 1.57 2005/02/10 20:16:54 fwarmerdam
+ * Make the pszStringField buffer for DBFReadAttribute() static char [256]
+ * as per bug 306.
*
- * Revision 1.17 1999/06/11 19:14:12 warmerda
- * Fixed some memory leaks.
+ * Revision 1.56 2005/02/10 20:07:56 fwarmerdam
+ * Fixed bug 305 in DBFCloneEmpty() - header length problem.
*
- * Revision 1.16 1999/06/11 19:04:11 warmerda
- * Remoted some unused variables.
+ * Revision 1.55 2004/09/26 20:23:46 fwarmerdam
+ * avoid warnings with rcsid and signed/unsigned stuff
*
- * Revision 1.15 1999/05/11 03:19:28 warmerda
- * added new Tuple api, and improved extension handling - add from candrsn
- *
- * Revision 1.14 1999/05/04 15:01:48 warmerda
- * Added 'F' support.
- *
- * Revision 1.13 1999/03/23 17:38:59 warmerda
- * DBFAddField() now actually does return the new field number, or -1 if
- * it fails.
- *
- * Revision 1.12 1999/03/06 02:54:46 warmerda
- * Added logic to convert shapefile name to dbf filename in DBFOpen()
- * for convenience.
- *
- * Revision 1.11 1998/12/31 15:30:34 warmerda
- * Improved the interchangability of numeric and string attributes. Add
- * white space trimming option for attributes.
- *
- * Revision 1.10 1998/12/03 16:36:44 warmerda
- * Use r+b instead of rb+ for binary access.
- *
- * Revision 1.9 1998/12/03 15:34:23 warmerda
- * Updated copyright message.
- *
- * Revision 1.8 1997/12/04 15:40:15 warmerda
- * Added newline character after field definitions.
- *
- * Revision 1.7 1997/03/06 14:02:10 warmerda
- * Ensure bUpdated is initialized.
- *
- * Revision 1.6 1996/02/12 04:54:41 warmerda
- * Ensure that DBFWriteAttribute() returns TRUE if it succeeds.
- *
- * Revision 1.5 1995/10/21 03:15:12 warmerda
- * Changed to use binary file access, and ensure that the
- * field name field is zero filled, and limited to 10 chars.
- *
- * Revision 1.4 1995/08/24 18:10:42 warmerda
- * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such
- * as on the Sun.
- *
- * Revision 1.3 1995/08/04 03:15:16 warmerda
- * Fixed up header.
- *
- * Revision 1.2 1995/08/04 03:14:43 warmerda
- * Added header.
+ * Revision 1.54 2004/09/15 16:26:10 fwarmerdam
+ * Treat all blank numeric fields as null too.
*/
-/*static char rcsid[] =
- "$Id: dbfopen.c,v 1.3 2006-07-13 03:27:54 robertl Exp $";*/
-
#include "shapefil.h"
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#if SHAPELIB_ENABLED
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
+SHP_CVSID("$Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $")
+
#ifndef FALSE
# define FALSE 0
# define TRUE 1
#endif
-static int nStringFieldLen = 0;
-static char * pszStringField = NULL;
-
/************************************************************************/
/* SfRealloc() */
/* */
abyHeader[0] = 0x03; /* memo field? - just copying */
- /* date updated on close, record count preset at zero */
+ /* write out a dummy date */
+ abyHeader[1] = 95; /* YY */
+ abyHeader[2] = 7; /* MM */
+ abyHeader[3] = 26; /* DD */
- abyHeader[8] = psDBF->nHeaderLength % 256;
- abyHeader[9] = psDBF->nHeaderLength / 256;
+ /* record count preset at zero */
+
+ abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256);
+ abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256);
- abyHeader[10] = psDBF->nRecordLength % 256;
- abyHeader[11] = psDBF->nRecordLength / 256;
+ abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256);
+ abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256);
+
+ abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver);
/* -------------------------------------------------------------------- */
/* Write the initial 32 byte file header, and all the field */
/* descriptions. */
/* -------------------------------------------------------------------- */
- fseek( psDBF->fp, 0, 0 );
- fwrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
- fwrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp );
+ psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
+ psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
+ psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields,
+ psDBF->fp );
/* -------------------------------------------------------------------- */
/* Write out the newline character if there is room for it. */
char cNewline;
cNewline = 0x0d;
- fwrite( &cNewline, 1, 1, psDBF->fp );
+ psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp );
}
}
/* Write out the current record if there is one. */
/************************************************************************/
-static void DBFFlushRecord( DBFHandle psDBF )
+static int DBFFlushRecord( DBFHandle psDBF )
{
- int nRecordOffset;
+ SAOffset nRecordOffset;
if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
{
psDBF->bCurrentRecordModified = FALSE;
- nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord
- + psDBF->nHeaderLength;
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord
+ + psDBF->nHeaderLength;
+
+ if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0
+ || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord,
+ psDBF->nRecordLength,
+ 1, psDBF->fp ) != 1 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "Failure writing DBF record %d.",
+ psDBF->nCurrentRecord );
+ psDBF->sHooks.Error( szMessage );
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* DBFLoadRecord() */
+/************************************************************************/
+
+static int DBFLoadRecord( DBFHandle psDBF, int iRecord )
+
+{
+ if( psDBF->nCurrentRecord != iRecord )
+ {
+ SAOffset nRecordOffset;
+
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "fseek(%ld) failed on DBF file.\n",
+ (long) nRecordOffset );
+ psDBF->sHooks.Error( szMessage );
+ return FALSE;
+ }
+
+ if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord,
+ psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "fread(%d) failed on DBF file.\n",
+ psDBF->nRecordLength );
+ psDBF->sHooks.Error( szMessage );
+ return FALSE;
+ }
- fseek( psDBF->fp, nRecordOffset, 0 );
- fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ psDBF->nCurrentRecord = iRecord;
}
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* DBFUpdateHeader() */
+/************************************************************************/
+
+void SHPAPI_CALL
+DBFUpdateHeader( DBFHandle psDBF )
+
+{
+ unsigned char abyFileHeader[32];
+
+ if( psDBF->bNoHeader )
+ DBFWriteHeader( psDBF );
+
+ DBFFlushRecord( psDBF );
+
+ psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
+ psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp );
+
+ abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256);
+ abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256);
+ abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256);
+ abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256);
+
+ psDBF->sHooks.FSeek( psDBF->fp, 0, 0 );
+ psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp );
+
+ psDBF->sHooks.FFlush( psDBF->fp );
}
/************************************************************************/
DBFHandle SHPAPI_CALL
DBFOpen( const char * pszFilename, const char * pszAccess )
+{
+ SAHooks sHooks;
+
+ SASetupDefaultHooks( &sHooks );
+
+ return DBFOpenLL( pszFilename, pszAccess, &sHooks );
+}
+
+/************************************************************************/
+/* DBFOpen() */
+/* */
+/* Open a .dbf file. */
+/************************************************************************/
+
+DBFHandle SHPAPI_CALL
+DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks )
+
{
DBFHandle psDBF;
- unsigned char *pabyBuf;
- int nFields, nHeadLen, nRecLen, iField, i;
+ SAFile pfCPG;
+ unsigned char *pabyBuf;
+ int nFields, nHeadLen, iField, i;
char *pszBasename, *pszFullname;
+ int nBufSize = 500;
/* -------------------------------------------------------------------- */
/* We only allow the access strings "rb" and "r+". */
sprintf( pszFullname, "%s.dbf", pszBasename );
psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
- psDBF->fp = fopen( pszFullname, pszAccess );
+ psDBF->fp = psHooks->FOpen( pszFullname, pszAccess );
+ memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
if( psDBF->fp == NULL )
{
sprintf( pszFullname, "%s.DBF", pszBasename );
- psDBF->fp = fopen(pszFullname, pszAccess );
+ psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess );
}
-
+
+ sprintf( pszFullname, "%s.cpg", pszBasename );
+ pfCPG = psHooks->FOpen( pszFullname, "r" );
+ if( pfCPG == NULL )
+ {
+ sprintf( pszFullname, "%s.CPG", pszBasename );
+ pfCPG = psHooks->FOpen( pszFullname, "r" );
+ }
+
free( pszBasename );
free( pszFullname );
if( psDBF->fp == NULL )
{
free( psDBF );
+ if( pfCPG ) psHooks->FClose( pfCPG );
return( NULL );
}
/* -------------------------------------------------------------------- */
/* Read Table Header info */
/* -------------------------------------------------------------------- */
- pabyBuf = (unsigned char *) malloc(500);
- if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 )
+ pabyBuf = (unsigned char *) malloc(nBufSize);
+ if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 )
{
- fclose( psDBF->fp );
+ psDBF->sHooks.FClose( psDBF->fp );
+ if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
free( pabyBuf );
free( psDBF );
return NULL;
pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;
psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
- psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256;
-
+ psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256;
+ psDBF->iLanguageDriver = pabyBuf[29];
+
+ if (nHeadLen < 32)
+ {
+ psDBF->sHooks.FClose( psDBF->fp );
+ if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
+ free( pabyBuf );
+ free( psDBF );
+ return NULL;
+ }
+
psDBF->nFields = nFields = (nHeadLen - 32) / 32;
- psDBF->pszCurrentRecord = (char *) malloc(nRecLen);
+ psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength);
+
+/* -------------------------------------------------------------------- */
+/* Figure out the code page from the LDID and CPG */
+/* -------------------------------------------------------------------- */
+
+ psDBF->pszCodePage = NULL;
+ if( pfCPG )
+ {
+ size_t n;
+ memset( pabyBuf, 0, nBufSize);
+ psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG );
+ n = strcspn( (char *) pabyBuf, "\n\r" );
+ if( n > 0 )
+ {
+ pabyBuf[n] = '\0';
+ psDBF->pszCodePage = (char *) malloc(n + 1);
+ memcpy( psDBF->pszCodePage, pabyBuf, n + 1 );
+ }
+ psDBF->sHooks.FClose( pfCPG );
+ }
+ if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 )
+ {
+ sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver );
+ psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1);
+ strcpy( psDBF->pszCodePage, (char *) pabyBuf );
+ }
/* -------------------------------------------------------------------- */
/* Read in Field Definitions */
pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen);
psDBF->pszHeader = (char *) pabyBuf;
- fseek( psDBF->fp, 32, 0 );
- if( fread( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 )
+ psDBF->sHooks.FSeek( psDBF->fp, 32, 0 );
+ if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 )
{
- fclose( psDBF->fp );
+ psDBF->sHooks.FClose( psDBF->fp );
free( pabyBuf );
+ free( psDBF->pszCurrentRecord );
free( psDBF );
return NULL;
}
}
else
{
+ psDBF->panFieldSize[iField] = pabyFInfo[16];
+ psDBF->panFieldDecimals[iField] = 0;
+
+/*
+** The following seemed to be used sometimes to handle files with long
+** string fields, but in other cases (such as bug 1202) the decimals field
+** just seems to indicate some sort of preferred formatting, not very
+** wide fields. So I have disabled this code. FrankW.
psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256;
psDBF->panFieldDecimals[iField] = 0;
+*/
}
psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
void SHPAPI_CALL
DBFClose(DBFHandle psDBF)
{
+ if( psDBF == NULL )
+ return;
+
/* -------------------------------------------------------------------- */
/* Write out header if not already written. */
/* -------------------------------------------------------------------- */
/* write access. */
/* -------------------------------------------------------------------- */
if( psDBF->bUpdated )
- {
- unsigned char abyFileHeader[32];
-
- fseek( psDBF->fp, 0, 0 );
- fread( abyFileHeader, 32, 1, psDBF->fp );
-
- abyFileHeader[1] = 95; /* YY */
- abyFileHeader[2] = 7; /* MM */
- abyFileHeader[3] = 26; /* DD */
-
- abyFileHeader[4] = psDBF->nRecords % 256;
- abyFileHeader[5] = (psDBF->nRecords/256) % 256;
- abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256;
- abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256;
-
- fseek( psDBF->fp, 0, 0 );
- fwrite( abyFileHeader, 32, 1, psDBF->fp );
- }
+ DBFUpdateHeader( psDBF );
/* -------------------------------------------------------------------- */
/* Close, and free resources. */
/* -------------------------------------------------------------------- */
- fclose( psDBF->fp );
+ psDBF->sHooks.FClose( psDBF->fp );
if( psDBF->panFieldOffset != NULL )
{
free( psDBF->pachFieldType );
}
+ if( psDBF->pszWorkField != NULL )
+ free( psDBF->pszWorkField );
+
free( psDBF->pszHeader );
free( psDBF->pszCurrentRecord );
+ free( psDBF->pszCodePage );
free( psDBF );
+}
- if( pszStringField != NULL )
- {
- free( pszStringField );
- pszStringField = NULL;
- nStringFieldLen = 0;
- }
+/************************************************************************/
+/* DBFCreate() */
+/* */
+/* Create a new .dbf file with default code page LDID/87 (0x57) */
+/************************************************************************/
+
+DBFHandle SHPAPI_CALL
+DBFCreate( const char * pszFilename )
+
+{
+ return DBFCreateEx( pszFilename, "LDID/87" ); // 0x57
+}
+
+/************************************************************************/
+/* DBFCreateEx() */
+/* */
+/* Create a new .dbf file. */
+/************************************************************************/
+
+DBFHandle SHPAPI_CALL
+DBFCreateEx( const char * pszFilename, const char* pszCodePage )
+
+{
+ SAHooks sHooks;
+
+ SASetupDefaultHooks( &sHooks );
+
+ return DBFCreateLL( pszFilename, pszCodePage , &sHooks );
}
/************************************************************************/
/************************************************************************/
DBFHandle SHPAPI_CALL
-DBFCreate( const char * pszFilename )
+DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks )
{
DBFHandle psDBF;
- FILE *fp;
+ SAFile fp;
char *pszFullname, *pszBasename;
- int i;
+ int i, ldid = -1;
+ char chZero = '\0';
/* -------------------------------------------------------------------- */
/* Compute the base (layer) name. If there is any extension */
pszFullname = (char *) malloc(strlen(pszBasename) + 5);
sprintf( pszFullname, "%s.dbf", pszBasename );
- free( pszBasename );
/* -------------------------------------------------------------------- */
/* Create the file. */
/* -------------------------------------------------------------------- */
- fp = fopen( pszFullname, "wb" );
+ fp = psHooks->FOpen( pszFullname, "wb" );
if( fp == NULL )
return( NULL );
+
+ psHooks->FWrite( &chZero, 1, 1, fp );
+ psHooks->FClose( fp );
- fputc( 0, fp );
- fclose( fp );
-
- fp = fopen( pszFullname, "rb+" );
+ fp = psHooks->FOpen( pszFullname, "rb+" );
if( fp == NULL )
return( NULL );
+
+ sprintf( pszFullname, "%s.cpg", pszBasename );
+ if( pszCodePage != NULL )
+ {
+ if( strncmp( pszCodePage, "LDID/", 5 ) == 0 )
+ {
+ ldid = atoi( pszCodePage + 5 );
+ if( ldid > 255 )
+ ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one
+ }
+ if( ldid < 0 )
+ {
+ SAFile fpCPG = psHooks->FOpen( pszFullname, "w" );
+ psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG );
+ psHooks->FClose( fpCPG );
+ }
+ }
+ if( pszCodePage == NULL || ldid >= 0 )
+ {
+ psHooks->Remove( pszFullname );
+ }
+
+ free( pszBasename );
free( pszFullname );
/* -------------------------------------------------------------------- */
/* Create the info structure. */
/* -------------------------------------------------------------------- */
- psDBF = (DBFHandle) malloc(sizeof(DBFInfo));
+ psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo));
+ memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) );
psDBF->fp = fp;
psDBF->nRecords = 0;
psDBF->nFields = 0;
psDBF->bNoHeader = TRUE;
+ psDBF->iLanguageDriver = ldid > 0 ? ldid : 0;
+ psDBF->pszCodePage = NULL;
+ if( pszCodePage )
+ {
+ psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 );
+ strcpy( psDBF->pszCodePage, pszCodePage );
+ }
+
return( psDBF );
}
/************************************************************************/
/* DBFAddField() */
/* */
-/* Add a field to a newly created .dbf file before any records */
-/* are written. */
+/* Add a field to a newly created .dbf or to an existing one */
/************************************************************************/
int SHPAPI_CALL
DBFAddField(DBFHandle psDBF, const char * pszFieldName,
DBFFieldType eType, int nWidth, int nDecimals )
+{
+ char chNativeType = 'C';
+
+ if( eType == FTLogical )
+ chNativeType = 'L';
+ else if( eType == FTString )
+ chNativeType = 'C';
+ else
+ chNativeType = 'N';
+
+ return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType,
+ nWidth, nDecimals );
+}
+
+/************************************************************************/
+/* DBFGetNullCharacter() */
+/************************************************************************/
+
+static char DBFGetNullCharacter(char chType)
+{
+ switch (chType)
+ {
+ case 'N':
+ case 'F':
+ return '*';
+ case 'D':
+ return '0';
+ case 'L':
+ return '?';
+ default:
+ return ' ';
+ }
+}
+
+/************************************************************************/
+/* DBFAddField() */
+/* */
+/* Add a field to a newly created .dbf file before any records */
+/* are written. */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals )
+
{
char *pszFInfo;
int i;
+ int nOldRecordLength, nOldHeaderLength;
+ char *pszRecord;
+ char chFieldFill;
+ SAOffset nRecordOffset;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return -1;
/* -------------------------------------------------------------------- */
/* Do some checking to ensure we can add records to this file. */
/* -------------------------------------------------------------------- */
- if( psDBF->nRecords > 0 )
- return( -1 );
-
- if( !psDBF->bNoHeader )
- return( -1 );
-
- if( eType != FTDouble && nDecimals != 0 )
- return( -1 );
-
if( nWidth < 1 )
return -1;
+ if( nWidth > 255 )
+ nWidth = 255;
+
+ nOldRecordLength = psDBF->nRecordLength;
+ nOldHeaderLength = psDBF->nHeaderLength;
+
/* -------------------------------------------------------------------- */
/* SfRealloc all the arrays larger to hold the additional field */
/* information. */
psDBF->nFields++;
psDBF->panFieldOffset = (int *)
- SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
+ SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
psDBF->panFieldSize = (int *)
- SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
+ SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
psDBF->panFieldDecimals = (int *)
- SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
+ SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
psDBF->pachFieldType = (char *)
- SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
+ SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
/* -------------------------------------------------------------------- */
/* Assign the new field information fields. */
psDBF->nRecordLength += nWidth;
psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
-
- if( eType == FTLogical )
- psDBF->pachFieldType[psDBF->nFields-1] = 'L';
- else if( eType == FTString )
- psDBF->pachFieldType[psDBF->nFields-1] = 'C';
- else
- psDBF->pachFieldType[psDBF->nFields-1] = 'N';
+ psDBF->pachFieldType[psDBF->nFields-1] = chType;
/* -------------------------------------------------------------------- */
/* Extend the required header information. */
pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
- if( eType == FTString )
+ if( chType == 'C' )
{
- pszFInfo[16] = nWidth % 256;
- pszFInfo[17] = nWidth / 256;
+ pszFInfo[16] = (unsigned char) (nWidth % 256);
+ pszFInfo[17] = (unsigned char) (nWidth / 256);
}
else
{
- pszFInfo[16] = nWidth;
- pszFInfo[17] = nDecimals;
+ pszFInfo[16] = (unsigned char) nWidth;
+ pszFInfo[17] = (unsigned char) nDecimals;
}
/* -------------------------------------------------------------------- */
/* Make the current record buffer appropriately larger. */
/* -------------------------------------------------------------------- */
psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
- psDBF->nRecordLength);
+ psDBF->nRecordLength);
+
+ /* we're done if dealing with new .dbf */
+ if( psDBF->bNoHeader )
+ return( psDBF->nFields - 1 );
+
+/* -------------------------------------------------------------------- */
+/* For existing .dbf file, shift records */
+/* -------------------------------------------------------------------- */
+
+ /* alloc record */
+ pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+
+ chFieldFill = DBFGetNullCharacter(chType);
+
+ for (i = psDBF->nRecords-1; i >= 0; --i)
+ {
+ nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ /* set new field's value to NULL */
+ memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
+
+ nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength;
+
+ /* move record to the new place*/
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ /* free record */
+ free(pszRecord);
+
+ /* force update of header with new header, record length and new field */
+ psDBF->bNoHeader = TRUE;
+ DBFUpdateHeader( psDBF );
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
return( psDBF->nFields-1 );
}
char chReqType )
{
- int nRecordOffset;
unsigned char *pabyRec;
void *pReturnField = NULL;
- static double dDoubleField;
-
/* -------------------------------------------------------------------- */
/* Verify selection. */
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
/* Have we read the record? */
/* -------------------------------------------------------------------- */
- if( psDBF->nCurrentRecord != hEntity )
- {
- DBFFlushRecord( psDBF );
-
- nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
-
- if( fseek( psDBF->fp, nRecordOffset, 0 ) != 0 )
- {
- fprintf( stderr, "fseek(%d) failed on DBF file.\n",
- nRecordOffset );
- return NULL;
- }
-
- if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength,
- 1, psDBF->fp ) != 1 )
- {
- fprintf( stderr, "fread(%d) failed on DBF file.\n",
- psDBF->nRecordLength );
- return NULL;
- }
-
- psDBF->nCurrentRecord = hEntity;
- }
+ if( !DBFLoadRecord( psDBF, hEntity ) )
+ return NULL;
pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
/* -------------------------------------------------------------------- */
-/* Ensure our field buffer is large enough to hold this buffer. */
+/* Ensure we have room to extract the target field. */
/* -------------------------------------------------------------------- */
- if( psDBF->panFieldSize[iField]+1 > nStringFieldLen )
+ if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength )
{
- nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10;
- pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen);
+ psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100;
+ if( psDBF->pszWorkField == NULL )
+ psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength);
+ else
+ psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField,
+ psDBF->nWorkFieldLength);
}
/* -------------------------------------------------------------------- */
/* Extract the requested field. */
/* -------------------------------------------------------------------- */
- strncpy( pszStringField,
+ strncpy( psDBF->pszWorkField,
((const char *) pabyRec) + psDBF->panFieldOffset[iField],
psDBF->panFieldSize[iField] );
- pszStringField[psDBF->panFieldSize[iField]] = '\0';
+ psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0';
- pReturnField = pszStringField;
+ pReturnField = psDBF->pszWorkField;
/* -------------------------------------------------------------------- */
/* Decode the field. */
/* -------------------------------------------------------------------- */
if( chReqType == 'N' )
{
- dDoubleField = atof(pszStringField);
+ psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField);
- pReturnField = &dDoubleField;
+ pReturnField = &(psDBF->dfDoubleField);
}
/* -------------------------------------------------------------------- */
{
char *pchSrc, *pchDst;
- pchDst = pchSrc = pszStringField;
+ pchDst = pchSrc = psDBF->pszWorkField;
while( *pchSrc == ' ' )
pchSrc++;
*(pchDst++) = *(pchSrc++);
*pchDst = '\0';
- while( pchDst != pszStringField && *(--pchDst) == ' ' )
+ while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' )
*pchDst = '\0';
}
#endif
return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) );
}
+
/************************************************************************/
-/* DBFIsAttributeNULL() */
-/* */
-/* Return TRUE if value for field is NULL. */
+/* DBFIsValueNULL() */
/* */
-/* Contributed by Jim Matthews. */
+/* Return TRUE if the passed string is NULL. */
/************************************************************************/
-int SHPAPI_CALL
-DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
-
+static int DBFIsValueNULL( char chType, const char* pszValue )
{
- const char *pszValue;
+ int i;
- pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
+ if( pszValue == NULL )
+ return TRUE;
- switch(psDBF->pachFieldType[iField])
+ switch(chType)
{
case 'N':
case 'F':
- /* NULL numeric fields have value "****************" */
- return pszValue[0] == '*';
+ /*
+ ** We accept all asterisks or all blanks as NULL
+ ** though according to the spec I think it should be all
+ ** asterisks.
+ */
+ if( pszValue[0] == '*' )
+ return TRUE;
+
+ for( i = 0; pszValue[i] != '\0'; i++ )
+ {
+ if( pszValue[i] != ' ' )
+ return FALSE;
+ }
+ return TRUE;
case 'D':
/* NULL date fields have value "00000000" */
return strncmp(pszValue,"00000000",8) == 0;
case 'L':
- /* NULL boolean fields have value "?" */
+ /* NULL boolean fields have value "?" */
return pszValue[0] == '?';
default:
}
/************************************************************************/
-/* DBFGetFieldCount() */
+/* DBFIsAttributeNULL() */
/* */
-/* Return the number of fields in this table. */
+/* Return TRUE if value for field is NULL. */
+/* */
+/* Contributed by Jim Matthews. */
/************************************************************************/
int SHPAPI_CALL
-DBFGetFieldCount( DBFHandle psDBF )
+DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField )
{
- return( psDBF->nFields );
-}
+ const char *pszValue;
-/************************************************************************/
-/* DBFGetRecordCount() */
+ pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
+
+ if( pszValue == NULL )
+ return TRUE;
+
+ return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue );
+}
+
+/************************************************************************/
+/* DBFGetFieldCount() */
+/* */
+/* Return the number of fields in this table. */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFGetFieldCount( DBFHandle psDBF )
+
+{
+ return( psDBF->nFields );
+}
+
+/************************************************************************/
+/* DBFGetRecordCount() */
/* */
/* Return the number of records in this table. */
/************************************************************************/
return( FTLogical);
else if( psDBF->pachFieldType[iField] == 'N'
- || psDBF->pachFieldType[iField] == 'F'
- || psDBF->pachFieldType[iField] == 'D' )
+ || psDBF->pachFieldType[iField] == 'F' )
{
- if( psDBF->panFieldDecimals[iField] > 0 )
+ if( psDBF->panFieldDecimals[iField] > 0
+ || psDBF->panFieldSize[iField] > 10 )
return( FTDouble );
else
return( FTInteger );
void * pValue )
{
- int nRecordOffset, i, j, nRetResult = TRUE;
+ int i, j, nRetResult = TRUE;
unsigned char *pabyRec;
char szSField[400], szFormat[20];
/* -------------------------------------------------------------------- */
if( hEntity == psDBF->nRecords )
{
- DBFFlushRecord( psDBF );
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
psDBF->nRecords++;
for( i = 0; i < psDBF->nRecordLength; i++ )
/* Is this an existing record, but different than the last one */
/* we accessed? */
/* -------------------------------------------------------------------- */
- if( psDBF->nCurrentRecord != hEntity )
- {
- DBFFlushRecord( psDBF );
-
- nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
-
- fseek( psDBF->fp, nRecordOffset, 0 );
- fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
-
- psDBF->nCurrentRecord = hEntity;
- }
+ if( !DBFLoadRecord( psDBF, hEntity ) )
+ return FALSE;
pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
/* -------------------------------------------------------------------- */
if( pValue == NULL )
{
- switch(psDBF->pachFieldType[iField])
- {
- case 'N':
- case 'F':
- /* NULL numeric fields have value "****************" */
- memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*',
- psDBF->panFieldSize[iField] );
- break;
-
- case 'D':
- /* NULL date fields have value "00000000" */
- memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0',
- psDBF->panFieldSize[iField] );
- break;
-
- case 'L':
- /* NULL boolean fields have value "?" */
- memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?',
- psDBF->panFieldSize[iField] );
- break;
-
- default:
- /* empty string fields are considered NULL */
- memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '\0',
- psDBF->panFieldSize[iField] );
- break;
- }
+ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
+ DBFGetNullCharacter(psDBF->pachFieldType[iField]),
+ psDBF->panFieldSize[iField] );
return TRUE;
}
{
int nWidth = psDBF->panFieldSize[iField];
- if( sizeof(szSField)-2 < nWidth )
+ if( (int) sizeof(szSField)-2 < nWidth )
nWidth = sizeof(szSField)-2;
sprintf( szFormat, "%%%dd", nWidth );
{
int nWidth = psDBF->panFieldSize[iField];
- if( sizeof(szSField)-2 < nWidth )
+ if( (int) sizeof(szSField)-2 < nWidth )
nWidth = sizeof(szSField)-2;
sprintf( szFormat, "%%%d.%df",
/* as is to the field position in the record. */
/************************************************************************/
-int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
+int SHPAPI_CALL
+DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField,
void * pValue )
{
- int nRecordOffset, i, j;
+ int i, j;
unsigned char *pabyRec;
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
if( hEntity == psDBF->nRecords )
{
- DBFFlushRecord( psDBF );
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
psDBF->nRecords++;
for( i = 0; i < psDBF->nRecordLength; i++ )
/* Is this an existing record, but different than the last one */
/* we accessed? */
/* -------------------------------------------------------------------- */
- if( psDBF->nCurrentRecord != hEntity )
- {
- DBFFlushRecord( psDBF );
-
- nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
-
- fseek( psDBF->fp, nRecordOffset, 0 );
- fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
-
- psDBF->nCurrentRecord = hEntity;
- }
+ if( !DBFLoadRecord( psDBF, hEntity ) )
+ return FALSE;
pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
{
- int nRecordOffset, i;
+ int i;
unsigned char *pabyRec;
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
if( hEntity == psDBF->nRecords )
{
- DBFFlushRecord( psDBF );
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
psDBF->nRecords++;
for( i = 0; i < psDBF->nRecordLength; i++ )
/* Is this an existing record, but different than the last one */
/* we accessed? */
/* -------------------------------------------------------------------- */
- if( psDBF->nCurrentRecord != hEntity )
- {
- DBFFlushRecord( psDBF );
-
- nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
-
- fseek( psDBF->fp, nRecordOffset, 0 );
- fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
-
- psDBF->nCurrentRecord = hEntity;
- }
+ if( !DBFLoadRecord( psDBF, hEntity ) )
+ return FALSE;
pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
}
/************************************************************************/
-/* DBFReadTuple() */
+/* DBFReadTuple() */
/* */
-/* Read one of the attribute fields of a record. */
+/* Read a complete record. Note that the result is only valid */
+/* till the next record read for any reason. */
/************************************************************************/
const char SHPAPI_CALL1(*)
DBFReadTuple(DBFHandle psDBF, int hEntity )
{
- int nRecordOffset;
- unsigned char *pabyRec;
- static char *pReturnTuple = NULL;
-
- static int nTupleLen = 0;
-
-/* -------------------------------------------------------------------- */
-/* Have we read the record? */
-/* -------------------------------------------------------------------- */
if( hEntity < 0 || hEntity >= psDBF->nRecords )
return( NULL );
- if( psDBF->nCurrentRecord != hEntity )
- {
- DBFFlushRecord( psDBF );
-
- nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
-
- fseek( psDBF->fp, nRecordOffset, 0 );
- fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
-
- psDBF->nCurrentRecord = hEntity;
- }
-
- pabyRec = (unsigned char *) psDBF->pszCurrentRecord;
+ if( !DBFLoadRecord( psDBF, hEntity ) )
+ return NULL;
- if ( nTupleLen < psDBF->nRecordLength) {
- nTupleLen = psDBF->nRecordLength;
- pReturnTuple = (char *) SfRealloc(pReturnTuple, psDBF->nRecordLength);
- }
-
- memcpy ( pReturnTuple, pabyRec, psDBF->nRecordLength );
-
- return( pReturnTuple );
+ return (const char *) psDBF->pszCurrentRecord;
}
/************************************************************************/
{
DBFHandle newDBF;
- newDBF = DBFCreate ( pszFilename );
+ newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage );
if ( newDBF == NULL ) return ( NULL );
- newDBF->pszHeader = (char *) malloc ( 32 * psDBF->nFields );
- memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields );
-
newDBF->nFields = psDBF->nFields;
newDBF->nRecordLength = psDBF->nRecordLength;
- newDBF->nHeaderLength = 32 * (psDBF->nFields+1);
+ newDBF->nHeaderLength = psDBF->nHeaderLength;
+ newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength );
+ memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength );
+
newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields );
memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields );
memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields );
memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
- newDBF->pachFieldType = (char *) malloc ( sizeof(int) * psDBF->nFields );
- memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(int) * psDBF->nFields );
+ newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields );
+ memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields );
newDBF->bNoHeader = TRUE;
newDBF->bUpdated = TRUE;
while (++i < len)
if (isalpha(string[i]) && islower(string[i]))
- string[i] = toupper ((int)string[i]);
+ string[i] = (char) toupper ((int)string[i]);
}
/************************************************************************/
}
return(-1);
}
-#endif
+
+/************************************************************************/
+/* DBFIsRecordDeleted() */
+/* */
+/* Returns TRUE if the indicated record is deleted, otherwise */
+/* it returns FALSE. */
+/************************************************************************/
+
+int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape )
+
+{
+/* -------------------------------------------------------------------- */
+/* Verify selection. */
+/* -------------------------------------------------------------------- */
+ if( iShape < 0 || iShape >= psDBF->nRecords )
+ return TRUE;
+
+/* -------------------------------------------------------------------- */
+/* Have we read the record? */
+/* -------------------------------------------------------------------- */
+ if( !DBFLoadRecord( psDBF, iShape ) )
+ return FALSE;
+
+/* -------------------------------------------------------------------- */
+/* '*' means deleted. */
+/* -------------------------------------------------------------------- */
+ return psDBF->pszCurrentRecord[0] == '*';
+}
+
+/************************************************************************/
+/* DBFMarkRecordDeleted() */
+/************************************************************************/
+
+int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
+ int bIsDeleted )
+
+{
+ char chNewFlag;
+
+/* -------------------------------------------------------------------- */
+/* Verify selection. */
+/* -------------------------------------------------------------------- */
+ if( iShape < 0 || iShape >= psDBF->nRecords )
+ return FALSE;
+
+/* -------------------------------------------------------------------- */
+/* Is this an existing record, but different than the last one */
+/* we accessed? */
+/* -------------------------------------------------------------------- */
+ if( !DBFLoadRecord( psDBF, iShape ) )
+ return FALSE;
+
+/* -------------------------------------------------------------------- */
+/* Assign value, marking record as dirty if it changes. */
+/* -------------------------------------------------------------------- */
+ if( bIsDeleted )
+ chNewFlag = '*';
+ else
+ chNewFlag = ' ';
+
+ if( psDBF->pszCurrentRecord[0] != chNewFlag )
+ {
+ psDBF->bCurrentRecordModified = TRUE;
+ psDBF->bUpdated = TRUE;
+ psDBF->pszCurrentRecord[0] = chNewFlag;
+ }
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* DBFGetCodePage */
+/************************************************************************/
+
+const char SHPAPI_CALL1(*)
+DBFGetCodePage(DBFHandle psDBF )
+{
+ if( psDBF == NULL )
+ return NULL;
+ return psDBF->pszCodePage;
+}
+
+/************************************************************************/
+/* DBFDeleteField() */
+/* */
+/* Remove a field from a .dbf file */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFDeleteField(DBFHandle psDBF, int iField)
+{
+ int nOldRecordLength, nOldHeaderLength;
+ int nDeletedFieldOffset, nDeletedFieldSize;
+ SAOffset nRecordOffset;
+ char* pszRecord;
+ int i, iRecord;
+
+ if (iField < 0 || iField >= psDBF->nFields)
+ return FALSE;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ /* get information about field to be deleted */
+ nOldRecordLength = psDBF->nRecordLength;
+ nOldHeaderLength = psDBF->nHeaderLength;
+ nDeletedFieldOffset = psDBF->panFieldOffset[iField];
+ nDeletedFieldSize = psDBF->panFieldSize[iField];
+
+ /* update fields info */
+ for (i = iField + 1; i < psDBF->nFields; i++)
+ {
+ psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize;
+ psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i];
+ psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i];
+ psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i];
+ }
+
+ /* resize fields arrays */
+ psDBF->nFields--;
+
+ psDBF->panFieldOffset = (int *)
+ SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
+
+ psDBF->panFieldSize = (int *)
+ SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
+
+ psDBF->panFieldDecimals = (int *)
+ SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
+
+ psDBF->pachFieldType = (char *)
+ SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
+
+ /* update header information */
+ psDBF->nHeaderLength -= 32;
+ psDBF->nRecordLength -= nDeletedFieldSize;
+
+ /* overwrite field information in header */
+ memmove(psDBF->pszHeader + iField*32,
+ psDBF->pszHeader + (iField+1)*32,
+ sizeof(char) * (psDBF->nFields - iField)*32);
+
+ psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
+
+ /* update size of current record appropriately */
+ psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
+ psDBF->nRecordLength);
+
+ /* we're done if we're dealing with not yet created .dbf */
+ if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
+ return TRUE;
+
+ /* force update of header with new header and record length */
+ psDBF->bNoHeader = TRUE;
+ DBFUpdateHeader( psDBF );
+
+ /* alloc record */
+ pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
+
+ /* shift records to their new positions */
+ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
+ {
+ nRecordOffset =
+ nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* move record in two steps */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp );
+ psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
+ nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize,
+ 1, psDBF->fp );
+
+ }
+
+ /* TODO: truncate file */
+
+ /* free record */
+ free(pszRecord);
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* DBFReorderFields() */
+/* */
+/* Reorder the fields of a .dbf file */
+/* */
+/* panMap must be exactly psDBF->nFields long and be a permutation */
+/* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
+/* code of DBFReorderFields. */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFReorderFields( DBFHandle psDBF, int* panMap )
+{
+ SAOffset nRecordOffset;
+ int i, iRecord;
+ int *panFieldOffsetNew;
+ int *panFieldSizeNew;
+ int *panFieldDecimalsNew;
+ char *pachFieldTypeNew;
+ char *pszHeaderNew;
+ char *pszRecord;
+ char *pszRecordNew;
+
+ if ( psDBF->nFields == 0 )
+ return TRUE;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields);
+ pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields);
+
+ /* shuffle fields definitions */
+ for(i=0; i < psDBF->nFields; i++)
+ {
+ panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
+ panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
+ pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
+ memcpy(pszHeaderNew + i * 32,
+ psDBF->pszHeader + panMap[i] * 32, 32);
+ }
+ panFieldOffsetNew[0] = 1;
+ for(i=1; i < psDBF->nFields; i++)
+ {
+ panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
+ }
+
+ free(psDBF->pszHeader);
+ psDBF->pszHeader = pszHeaderNew;
+
+ /* we're done if we're dealing with not yet created .dbf */
+ if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
+ {
+ /* force update of header with new header and record length */
+ psDBF->bNoHeader = TRUE;
+ DBFUpdateHeader( psDBF );
+
+ /* alloc record */
+ pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+ pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+
+ /* shuffle fields in records */
+ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
+ {
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+ pszRecordNew[0] = pszRecord[0];
+
+ for(i=0; i < psDBF->nFields; i++)
+ {
+ memcpy(pszRecordNew + panFieldOffsetNew[i],
+ pszRecord + psDBF->panFieldOffset[panMap[i]],
+ psDBF->panFieldSize[panMap[i]]);
+ }
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ /* free record */
+ free(pszRecord);
+ free(pszRecordNew);
+ }
+
+ free(psDBF->panFieldOffset);
+ free(psDBF->panFieldSize);
+ free(psDBF->panFieldDecimals);
+ free(psDBF->pachFieldType);
+
+ psDBF->panFieldOffset = panFieldOffsetNew;
+ psDBF->panFieldSize = panFieldSizeNew;
+ psDBF->panFieldDecimals =panFieldDecimalsNew;
+ psDBF->pachFieldType = pachFieldTypeNew;
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
+
+
+/************************************************************************/
+/* DBFAlterFieldDefn() */
+/* */
+/* Alter a field definition in a .dbf file */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals )
+{
+ int i;
+ int iRecord;
+ int nOffset;
+ int nOldWidth;
+ int nOldRecordLength;
+ int nRecordOffset;
+ char* pszFInfo;
+ char chOldType;
+ int bIsNULL;
+ char chFieldFill;
+
+ if (iField < 0 || iField >= psDBF->nFields)
+ return FALSE;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ chFieldFill = DBFGetNullCharacter(chType);
+
+ chOldType = psDBF->pachFieldType[iField];
+ nOffset = psDBF->panFieldOffset[iField];
+ nOldWidth = psDBF->panFieldSize[iField];
+ nOldRecordLength = psDBF->nRecordLength;
+
+/* -------------------------------------------------------------------- */
+/* Do some checking to ensure we can add records to this file. */
+/* -------------------------------------------------------------------- */
+ if( nWidth < 1 )
+ return -1;
+
+ if( nWidth > 255 )
+ nWidth = 255;
+
+/* -------------------------------------------------------------------- */
+/* Assign the new field information fields. */
+/* -------------------------------------------------------------------- */
+ psDBF->panFieldSize[iField] = nWidth;
+ psDBF->panFieldDecimals[iField] = nDecimals;
+ psDBF->pachFieldType[iField] = chType;
+
+/* -------------------------------------------------------------------- */
+/* Update the header information. */
+/* -------------------------------------------------------------------- */
+ pszFInfo = psDBF->pszHeader + 32 * iField;
+
+ for( i = 0; i < 32; i++ )
+ pszFInfo[i] = '\0';
+
+ if( (int) strlen(pszFieldName) < 10 )
+ strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
+ else
+ strncpy( pszFInfo, pszFieldName, 10);
+
+ pszFInfo[11] = psDBF->pachFieldType[iField];
+
+ if( chType == 'C' )
+ {
+ pszFInfo[16] = (unsigned char) (nWidth % 256);
+ pszFInfo[17] = (unsigned char) (nWidth / 256);
+ }
+ else
+ {
+ pszFInfo[16] = (unsigned char) nWidth;
+ pszFInfo[17] = (unsigned char) nDecimals;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Update offsets */
+/* -------------------------------------------------------------------- */
+ if (nWidth != nOldWidth)
+ {
+ for (i = iField + 1; i < psDBF->nFields; i++)
+ psDBF->panFieldOffset[i] += nWidth - nOldWidth;
+ psDBF->nRecordLength += nWidth - nOldWidth;
+
+ psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
+ psDBF->nRecordLength);
+ }
+
+ /* we're done if we're dealing with not yet created .dbf */
+ if ( psDBF->bNoHeader && psDBF->nRecords == 0 )
+ return TRUE;
+
+ /* force update of header with new header and record length */
+ psDBF->bNoHeader = TRUE;
+ DBFUpdateHeader( psDBF );
+
+ if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
+ {
+ char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
+ char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
+
+ pszOldField[nOldWidth] = 0;
+
+ /* move records to their new positions */
+ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
+ {
+ nRecordOffset =
+ nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
+ bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
+
+ if (nWidth != nOldWidth)
+ {
+ if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ')
+ {
+ /* Strip leading spaces when truncating a numeric field */
+ memmove( pszRecord + nOffset,
+ pszRecord + nOffset + nOldWidth - nWidth,
+ nWidth );
+ }
+ if (nOffset + nOldWidth < nOldRecordLength)
+ {
+ memmove( pszRecord + nOffset + nWidth,
+ pszRecord + nOffset + nOldWidth,
+ nOldRecordLength - (nOffset + nOldWidth));
+ }
+ }
+
+ /* Convert null value to the appropriate value of the new type */
+ if (bIsNULL)
+ {
+ memset( pszRecord + nOffset, chFieldFill, nWidth);
+ }
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ free(pszRecord);
+ free(pszOldField);
+ }
+ else if (nWidth > nOldWidth)
+ {
+ char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+ char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
+
+ pszOldField[nOldWidth] = 0;
+
+ /* move records to their new positions */
+ for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
+ {
+ nRecordOffset =
+ nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
+ bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
+
+ if (nOffset + nOldWidth < nOldRecordLength)
+ {
+ memmove( pszRecord + nOffset + nWidth,
+ pszRecord + nOffset + nOldWidth,
+ nOldRecordLength - (nOffset + nOldWidth));
+ }
+
+ /* Convert null value to the appropriate value of the new type */
+ if (bIsNULL)
+ {
+ memset( pszRecord + nOffset, chFieldFill, nWidth);
+ }
+ else
+ {
+ if ((chOldType == 'N' || chOldType == 'F'))
+ {
+ /* Add leading spaces when expanding a numeric field */
+ memmove( pszRecord + nOffset + nWidth - nOldWidth,
+ pszRecord + nOffset, nOldWidth );
+ memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
+ }
+ else
+ {
+ /* Add trailing spaces */
+ memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
+ }
+ }
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ free(pszRecord);
+ free(pszOldField);
+ }
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
--- /dev/null
+/******************************************************************************
+ * $Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $
+ *
+ * Project: Shapelib
+ * Purpose: Default implementation of file io based on stdio.
+ * Author: Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2007, Frank Warmerdam
+ *
+ * This software is available under the following "MIT Style" license,
+ * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This
+ * option is discussed in more detail in shapelib.html.
+ *
+ * --
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log: safileio.c,v $
+ * Revision 1.4 2008-01-16 20:05:14 bram
+ * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks
+ * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this
+ * is only available on the Windows platform that decodes the UTF-8 filenames to wide
+ * character strings and feeds them to _wfopen and _wremove.
+ *
+ * Revision 1.3 2007/12/18 18:28:11 bram
+ * - create hook for client specific atof (bugzilla ticket 1615)
+ * - check for NULL handle before closing cpCPG file, and close after reading.
+ *
+ * Revision 1.2 2007/12/15 20:25:30 bram
+ * dbfopen.c now reads the Code Page information from the DBF file, and exports
+ * this information as a string through the DBFGetCodePage function. This is
+ * either the number from the LDID header field ("LDID/<number>") or as the
+ * content of an accompanying .CPG file. When creating a DBF file, the code can
+ * be set using DBFCreateEx.
+ *
+ * Revision 1.1 2007/12/06 06:56:41 fwarmerdam
+ * new
+ *
+ */
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+SHP_CVSID("$Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $");
+
+#ifdef SHPAPI_UTF8_HOOKS
+# ifdef SHPAPI_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+# pragma comment(lib, "kernel32.lib")
+# endif
+#endif
+
+/************************************************************************/
+/* SADFOpen() */
+/************************************************************************/
+
+SAFile SADFOpen( const char *pszFilename, const char *pszAccess )
+
+{
+ return (SAFile) fopen( pszFilename, pszAccess );
+}
+
+/************************************************************************/
+/* SADFRead() */
+/************************************************************************/
+
+SAOffset SADFRead( void *p, SAOffset size, SAOffset nmemb, SAFile file )
+
+{
+ return (SAOffset) fread( p, (size_t) size, (size_t) nmemb,
+ (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFWrite() */
+/************************************************************************/
+
+SAOffset SADFWrite( void *p, SAOffset size, SAOffset nmemb, SAFile file )
+
+{
+ return (SAOffset) fwrite( p, (size_t) size, (size_t) nmemb,
+ (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFSeek() */
+/************************************************************************/
+
+SAOffset SADFSeek( SAFile file, SAOffset offset, int whence )
+
+{
+ return (SAOffset) fseek( (FILE *) file, (long) offset, whence );
+}
+
+/************************************************************************/
+/* SADFTell() */
+/************************************************************************/
+
+SAOffset SADFTell( SAFile file )
+
+{
+ return (SAOffset) ftell( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFFlush() */
+/************************************************************************/
+
+int SADFFlush( SAFile file )
+
+{
+ return fflush( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFClose() */
+/************************************************************************/
+
+int SADFClose( SAFile file )
+
+{
+ return fclose( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFClose() */
+/************************************************************************/
+
+int SADRemove( const char *filename )
+
+{
+ return remove( filename );
+}
+
+/************************************************************************/
+/* SADError() */
+/************************************************************************/
+
+void SADError( const char *message )
+
+{
+ fprintf( stderr, "%s\n", message );
+}
+
+/************************************************************************/
+/* SASetupDefaultHooks() */
+/************************************************************************/
+
+void SASetupDefaultHooks( SAHooks *psHooks )
+
+{
+ psHooks->FOpen = SADFOpen;
+ psHooks->FRead = SADFRead;
+ psHooks->FWrite = SADFWrite;
+ psHooks->FSeek = SADFSeek;
+ psHooks->FTell = SADFTell;
+ psHooks->FFlush = SADFFlush;
+ psHooks->FClose = SADFClose;
+ psHooks->Remove = SADRemove;
+
+ psHooks->Error = SADError;
+ psHooks->Atof = atof;
+}
+
+
+
+
+#ifdef SHPAPI_WINDOWS
+
+/************************************************************************/
+/* Utf8ToWideChar */
+/************************************************************************/
+
+const wchar_t* Utf8ToWideChar( const char *pszFilename )
+{
+ int nMulti, nWide;
+ wchar_t *pwszFileName;
+
+ nMulti = strlen(pszFilename) + 1;
+ nWide = MultiByteToWideChar( CP_UTF8, 0, pszFilename, nMulti, 0, 0);
+ if( nWide == 0 )
+ {
+ return NULL;
+ }
+ pwszFileName = (wchar_t*) malloc(nWide * sizeof(wchar_t));
+ if ( pwszFileName == NULL )
+ {
+ return NULL;
+ }
+ if( MultiByteToWideChar( CP_UTF8, 0, pszFilename, nMulti, pwszFileName, nWide ) == 0 )
+ {
+ free( pwszFileName );
+ return NULL;
+ }
+ return pwszFileName;
+}
+
+/************************************************************************/
+/* SAUtf8WFOpen */
+/************************************************************************/
+
+SAFile SAUtf8WFOpen( const char *pszFilename, const char *pszAccess )
+{
+ SAFile file = NULL;
+ const wchar_t *pwszFileName, *pwszAccess;
+ pwszFileName = Utf8ToWideChar( pszFilename );
+ pwszAccess = Utf8ToWideChar( pszAccess );
+ if( pwszFileName != NULL && pwszFileName != NULL)
+ {
+ file = (SAFile) _wfopen( pwszFileName, pwszAccess );
+ }
+ free ((wchar_t*) pwszFileName);
+ free ((wchar_t*) pwszAccess);
+ return file;
+}
+
+/************************************************************************/
+/* SAUtf8WRemove() */
+/************************************************************************/
+
+int SAUtf8WRemove( const char *pszFilename )
+{
+ const wchar_t *pwszFileName = Utf8ToWideChar( pszFilename );
+ int rc = -1;
+ if( pwszFileName != NULL )
+ {
+ rc = _wremove( pwszFileName );
+ }
+ free ((wchar_t*) pwszFileName);
+ return rc;
+}
+
+#endif
+
+#ifdef SHPAPI_UTF8_HOOKS
+
+/************************************************************************/
+/* SASetupUtf8Hooks() */
+/************************************************************************/
+
+void SASetupUtf8Hooks( SAHooks *psHooks )
+{
+#ifdef SHPAPI_WINDOWS
+ psHooks->FOpen = SAUtf8WFOpen;
+ psHooks->Remove = SAUtf8WRemove;
+#else
+# error "no implementations of UTF-8 hooks available for this platform"
+#endif
+ psHooks->FRead = SADFRead;
+ psHooks->FWrite = SADFWrite;
+ psHooks->FSeek = SADFSeek;
+ psHooks->FTell = SADFTell;
+ psHooks->FFlush = SADFFlush;
+ psHooks->FClose = SADFClose;
+
+ psHooks->Error = SADError;
+ psHooks->Atof = atof;
+}
+
+#endif
-#ifndef _SHAPEFILE_H_INCLUDED
-#define _SHAPEFILE_H_INCLUDED
+#ifndef SHAPEFILE_H_INCLUDED
+#define SHAPEFILE_H_INCLUDED
/******************************************************************************
- * $Id: shapefil.h,v 1.2 2004-09-27 01:13:58 robertl Exp $
+ * $Id: shapefil.h,v 1.52 2011-12-11 22:26:46 fwarmerdam Exp $
*
* Project: Shapelib
* Purpose: Primary include file for Shapelib.
* DEALINGS IN THE SOFTWARE.
******************************************************************************
*
- * $Log: not supported by cvs2svn $
- * Revision 1.1 2004/09/20 17:22:55 robertl
- * Bring in shapefil.h.
+ * $Log: shapefil.h,v $
+ * Revision 1.52 2011-12-11 22:26:46 fwarmerdam
+ * upgrade .qix access code to use SAHooks (gdal #3365)
*
- * Revision 1.26 2002/09/29 00:00:08 warmerda
- * added FTLogical and logical attribute read/write calls
+ * Revision 1.51 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
*
- * Revision 1.25 2002/05/07 13:46:30 warmerda
- * added DBFWriteAttributeDirectly().
+ * Revision 1.50 2011-05-13 17:35:17 fwarmerdam
+ * added DBFReorderFields() and DBFAlterFields() functions (from Even)
*
- * Revision 1.24 2002/04/10 16:59:54 warmerda
- * added SHPRewindObject
+ * Revision 1.49 2011-04-16 14:38:21 fwarmerdam
+ * avoid warnings with gcc on SHP_CVSID
*
- * Revision 1.23 2002/01/15 14:36:07 warmerda
- * updated email address
+ * Revision 1.48 2010-08-27 23:42:52 fwarmerdam
+ * add SHPAPI_CALL attribute in code
*
- * Revision 1.22 2002/01/15 14:32:00 warmerda
- * try to improve SHPAPI_CALL docs
+ * Revision 1.47 2010-01-28 11:34:34 fwarmerdam
+ * handle the shape file length limits more gracefully (#3236)
*
- * Revision 1.21 2001/11/01 16:29:55 warmerda
- * move pabyRec into SHPInfo for thread safety
+ * Revision 1.46 2008-11-12 14:28:15 fwarmerdam
+ * DBFCreateField() now works on files with records
*
- * Revision 1.20 2001/07/20 13:06:02 warmerda
- * fixed SHPAPI attribute for SHPTreeFindLikelyShapes
+ * Revision 1.45 2008/11/11 17:47:10 fwarmerdam
+ * added DBFDeleteField() function
*
- * Revision 1.19 2001/05/31 19:20:13 warmerda
- * added DBFGetFieldIndex()
+ * Revision 1.44 2008/01/16 20:05:19 bram
+ * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks
+ * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this
+ * is only available on the Windows platform that decodes the UTF-8 filenames to wide
+ * character strings and feeds them to _wfopen and _wremove.
*
- * Revision 1.18 2001/05/31 18:15:40 warmerda
- * Added support for NULL fields in DBF files
+ * Revision 1.43 2008/01/10 16:35:30 fwarmerdam
+ * avoid _ prefix on #defined symbols (bug 1840)
*
- * Revision 1.17 2001/05/23 13:36:52 warmerda
- * added use of SHPAPI_CALL
+ * Revision 1.42 2007/12/18 18:28:14 bram
+ * - create hook for client specific atof (bugzilla ticket 1615)
+ * - check for NULL handle before closing cpCPG file, and close after reading.
*
- * Revision 1.16 2000/09/25 14:15:59 warmerda
- * added DBFGetNativeFieldType()
+ * Revision 1.41 2007/12/15 20:25:32 bram
+ * dbfopen.c now reads the Code Page information from the DBF file, and exports
+ * this information as a string through the DBFGetCodePage function. This is
+ * either the number from the LDID header field ("LDID/<number>") or as the
+ * content of an accompanying .CPG file. When creating a DBF file, the code can
+ * be set using DBFCreateEx.
*
- * Revision 1.15 2000/02/16 16:03:51 warmerda
- * added null shape support
+ * Revision 1.40 2007/12/06 07:00:25 fwarmerdam
+ * dbfopen now using SAHooks for fileio
*
- * Revision 1.14 1999/11/05 14:12:05 warmerda
- * updated license terms
+ * Revision 1.39 2007/12/04 20:37:56 fwarmerdam
+ * preliminary implementation of hooks api for io and errors
*
- * Revision 1.13 1999/06/02 18:24:21 warmerda
- * added trimming code
+ * Revision 1.38 2007/11/21 22:39:56 fwarmerdam
+ * close shx file in readonly mode (GDAL #1956)
*
- * Revision 1.12 1999/06/02 17:56:12 warmerda
- * added quad'' subnode support for trees
+ * Revision 1.37 2007/10/27 03:31:14 fwarmerdam
+ * limit default depth of tree to 12 levels (gdal ticket #1594)
*
- * Revision 1.11 1999/05/18 19:11:11 warmerda
- * Added example searching capability
+ * Revision 1.36 2007/09/10 23:33:15 fwarmerdam
+ * Upstreamed support for visibility flag in SHPAPI_CALL for the needs
+ * of GDAL (gdal ticket #1810).
*
- * Revision 1.10 1999/05/18 17:49:38 warmerda
- * added initial quadtree support
+ * Revision 1.35 2007/09/03 19:48:10 fwarmerdam
+ * move DBFReadAttribute() static dDoubleField into dbfinfo
*
- * Revision 1.9 1999/05/11 03:19:28 warmerda
- * added new Tuple api, and improved extension handling - add from candrsn
+ * Revision 1.34 2006/06/17 15:33:32 fwarmerdam
+ * added pszWorkField - bug 1202 (rso)
*
- * Revision 1.8 1999/03/23 17:22:27 warmerda
- * Added extern "C" protection for C++ users of shapefil.h.
+ * Revision 1.33 2006/02/15 01:14:30 fwarmerdam
+ * added DBFAddNativeFieldType
*
- * Revision 1.7 1998/12/31 15:31:07 warmerda
- * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options.
+ * Revision 1.32 2006/01/26 15:07:32 fwarmerdam
+ * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
*
- * Revision 1.6 1998/12/03 15:48:15 warmerda
- * Added SHPCalculateExtents().
+ * Revision 1.31 2006/01/05 01:27:27 fwarmerdam
+ * added dbf deletion mark/fetch
*
- * Revision 1.5 1998/11/09 20:57:16 warmerda
- * Altered SHPGetInfo() call.
+ * Revision 1.30 2005/01/03 22:30:13 fwarmerdam
+ * added support for saved quadtrees
*
- * Revision 1.4 1998/11/09 20:19:33 warmerda
- * Added 3D support, and use of SHPObject.
+ * Revision 1.29 2004/09/26 20:09:35 fwarmerdam
+ * avoid rcsid warnings
*
- * Revision 1.3 1995/08/23 02:24:05 warmerda
- * Added support for reading bounds.
+ * Revision 1.28 2003/12/29 06:02:18 fwarmerdam
+ * added cpl_error.h option
*
- * Revision 1.2 1995/08/04 03:17:39 warmerda
- * Added header.
+ * Revision 1.27 2003/04/21 18:30:37 warmerda
+ * added header write/update public methods
*
+ * Revision 1.26 2002/09/29 00:00:08 warmerda
+ * added FTLogical and logical attribute read/write calls
+ *
+ * Revision 1.25 2002/05/07 13:46:30 warmerda
+ * added DBFWriteAttributeDirectly().
+ *
+ * Revision 1.24 2002/04/10 16:59:54 warmerda
+ * added SHPRewindObject
+ *
+ * Revision 1.23 2002/01/15 14:36:07 warmerda
+ * updated email address
+ *
+ * Revision 1.22 2002/01/15 14:32:00 warmerda
+ * try to improve SHPAPI_CALL docs
*/
#include <stdio.h>
/* is disabled. */
/* -------------------------------------------------------------------- */
#define DISABLE_MULTIPATCH_MEASURE
-
+
/* -------------------------------------------------------------------- */
/* SHPAPI_CALL */
/* */
#endif
#ifndef SHPAPI_CALL
-# define SHPAPI_CALL
+# if defined(USE_GCC_VISIBILITY_FLAG)
+# define SHPAPI_CALL __attribute__ ((visibility("default")))
+# define SHPAPI_CALL1(x) __attribute__ ((visibility("default"))) x
+# else
+# define SHPAPI_CALL
+# endif
#endif
#ifndef SHPAPI_CALL1
# define SHPAPI_CALL1(x) x SHPAPI_CALL
#endif
+/* -------------------------------------------------------------------- */
+/* Macros for controlling CVSID and ensuring they don't appear */
+/* as unreferenced variables resulting in lots of warnings. */
+/* -------------------------------------------------------------------- */
+#ifndef DISABLE_CVSID
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define SHP_CVSID(string) static char cpl_cvsid[] __attribute__((used)) = string;
+# else
+# define SHP_CVSID(string) static char cpl_cvsid[] = string; \
+static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); }
+# endif
+#else
+# define SHP_CVSID(string)
+#endif
+
+/* -------------------------------------------------------------------- */
+/* On some platforms, additional file IO hooks are defined that */
+/* UTF-8 encoded filenames Unicode filenames */
+/* -------------------------------------------------------------------- */
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+# define SHPAPI_WINDOWS
+# define SHPAPI_UTF8_HOOKS
+#endif
+
+/* -------------------------------------------------------------------- */
+/* IO/Error hook functions. */
+/* -------------------------------------------------------------------- */
+typedef int *SAFile;
+
+#ifndef SAOffset
+typedef unsigned long SAOffset;
+#endif
+
+typedef struct {
+ SAFile (*FOpen) ( const char *filename, const char *access);
+ SAOffset (*FRead) ( void *p, SAOffset size, SAOffset nmemb, SAFile file);
+ SAOffset (*FWrite)( void *p, SAOffset size, SAOffset nmemb, SAFile file);
+ SAOffset (*FSeek) ( SAFile file, SAOffset offset, int whence );
+ SAOffset (*FTell) ( SAFile file );
+ int (*FFlush)( SAFile file );
+ int (*FClose)( SAFile file );
+ int (*Remove) ( const char *filename );
+
+ void (*Error) ( const char *message );
+ double (*Atof) ( const char *str );
+} SAHooks;
+
+void SHPAPI_CALL SASetupDefaultHooks( SAHooks *psHooks );
+#ifdef SHPAPI_UTF8_HOOKS
+void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks );
+#endif
+
/************************************************************************/
/* SHP Support. */
/************************************************************************/
typedef struct
{
- FILE *fpSHP;
- FILE *fpSHX;
+ SAHooks sHooks;
+
+ SAFile fpSHP;
+ SAFile fpSHX;
int nShapeType; /* SHPT_* */
- int nFileSize; /* SHP file */
+ unsigned int nFileSize; /* SHP file */
int nRecords;
int nMaxRecords;
- int *panRecOffset;
- int *panRecSize;
+ unsigned int *panRecOffset;
+ unsigned int *panRecSize;
double adBoundsMin[4];
double adBoundsMax[4];
double dfYMax;
double dfZMax;
double dfMMax;
+
+ int bMeasureIsUsed;
} SHPObject;
/* -------------------------------------------------------------------- */
/* SHP API Prototypes */
/* -------------------------------------------------------------------- */
+
+/* If pszAccess is read-only, the fpSHX field of the returned structure */
+/* will be NULL as it is not necessary to keep the SHX file open */
SHPHandle SHPAPI_CALL
SHPOpen( const char * pszShapeFile, const char * pszAccess );
+SHPHandle SHPAPI_CALL
+ SHPOpenLL( const char *pszShapeFile, const char *pszAccess,
+ SAHooks *psHooks );
SHPHandle SHPAPI_CALL
SHPCreate( const char * pszShapeFile, int nShapeType );
+SHPHandle SHPAPI_CALL
+ SHPCreateLL( const char * pszShapeFile, int nShapeType,
+ SAHooks *psHooks );
void SHPAPI_CALL
SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType,
double * padfMinBound, double * padfMaxBound );
void SHPAPI_CALL
SHPComputeExtents( SHPObject * psObject );
SHPObject SHPAPI_CALL1(*)
- SHPCreateObject( int nSHPType, int nShapeId,
- int nParts, int * panPartStart, int * panPartType,
- int nVertices, const double * padfX, const double * padfY,
+ SHPCreateObject( int nSHPType, int nShapeId, int nParts,
+ const int * panPartStart, const int * panPartType,
+ int nVertices,
+ const double * padfX, const double * padfY,
const double * padfZ, const double * padfM );
SHPObject SHPAPI_CALL1(*)
SHPCreateSimpleObject( int nSHPType, int nVertices,
- const double * padfX, const double * padfY, const double * padfZ );
+ const double * padfX,
+ const double * padfY,
+ const double * padfZ );
int SHPAPI_CALL
SHPRewindObject( SHPHandle hSHP, SHPObject * psObject );
-void SHPAPI_CALL
- SHPClose( SHPHandle hSHP );
+void SHPAPI_CALL SHPClose( SHPHandle hSHP );
+void SHPAPI_CALL SHPWriteHeader( SHPHandle hSHP );
const char SHPAPI_CALL1(*)
SHPTypeName( int nSHPType );
/* this can be two or four for binary or quad tree */
#define MAX_SUBNODE 4
+/* upper limit of tree levels for automatic estimation */
+#define MAX_DEFAULT_TREE_DEPTH 12
+
typedef struct shape_tree_node
{
/* region covered by this node */
int nMaxDepth;
int nDimension;
+ int nTotalCount;
SHPTreeNode *psRoot;
} SHPTree;
int SHPAPI_CALL
SHPWriteTree( SHPTree *hTree, const char * pszFilename );
-SHPTree SHPAPI_CALL
- SHPReadTree( const char * pszFilename );
-int SHPAPI_CALL
- SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject );
int SHPAPI_CALL
SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject );
int SHPAPI_CALL
int SHPAPI_CALL
SHPCheckBoundsOverlap( double *, double *, double *, double *, int );
+int SHPAPI_CALL1(*)
+SHPSearchDiskTree( FILE *fp,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount );
+
+
+typedef struct SHPDiskTreeInfo* SHPTreeDiskHandle;
+
+SHPTreeDiskHandle SHPAPI_CALL
+ SHPOpenDiskTree( const char* pszQIXFilename,
+ SAHooks *psHooks );
+
+void SHPAPI_CALL
+ SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree );
+
+int SHPAPI_CALL1(*)
+SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount );
+
+int SHPAPI_CALL
+ SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, SAHooks *psHooks );
+
/************************************************************************/
/* DBF Support. */
/************************************************************************/
typedef struct
{
- FILE *fp;
+ SAHooks sHooks;
+
+ SAFile fp;
int nRecords;
int nCurrentRecord;
int bCurrentRecordModified;
char *pszCurrentRecord;
+
+ int nWorkFieldLength;
+ char *pszWorkField;
int bNoHeader;
int bUpdated;
+
+ double dfDoubleField;
+
+ int iLanguageDriver;
+ char *pszCodePage;
} DBFInfo;
typedef DBFInfo * DBFHandle;
#define XBASE_FLDHDR_SZ 32
+
DBFHandle SHPAPI_CALL
DBFOpen( const char * pszDBFFile, const char * pszAccess );
+DBFHandle SHPAPI_CALL
+ DBFOpenLL( const char * pszDBFFile, const char * pszAccess,
+ SAHooks *psHooks );
DBFHandle SHPAPI_CALL
DBFCreate( const char * pszDBFFile );
+DBFHandle SHPAPI_CALL
+ DBFCreateEx( const char * pszDBFFile, const char * pszCodePage );
+DBFHandle SHPAPI_CALL
+ DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks );
int SHPAPI_CALL
DBFGetFieldCount( DBFHandle psDBF );
DBFAddField( DBFHandle hDBF, const char * pszFieldName,
DBFFieldType eType, int nWidth, int nDecimals );
+int SHPAPI_CALL
+ DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals );
+
+int SHPAPI_CALL
+ DBFDeleteField( DBFHandle hDBF, int iField );
+
+int SHPAPI_CALL
+ DBFReorderFields( DBFHandle psDBF, int* panMap );
+
+int SHPAPI_CALL
+ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals );
+
DBFFieldType SHPAPI_CALL
DBFGetFieldInfo( DBFHandle psDBF, int iField,
char * pszFieldName, int * pnWidth, int * pnDecimals );
int SHPAPI_CALL
DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple );
+int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape );
+int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape,
+ int bIsDeleted );
+
DBFHandle SHPAPI_CALL
DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename );
void SHPAPI_CALL
DBFClose( DBFHandle hDBF );
+void SHPAPI_CALL
+ DBFUpdateHeader( DBFHandle hDBF );
char SHPAPI_CALL
DBFGetNativeFieldType( DBFHandle hDBF, int iField );
+const char SHPAPI_CALL1(*)
+ DBFGetCodePage(DBFHandle psDBF );
+
#ifdef __cplusplus
}
#endif
-#endif /* ndef _SHAPEFILE_H_INCLUDED */
+#endif /* ndef SHAPEFILE_H_INCLUDED */
<html>
<head>
<title>Shapefile C Library V1.2</title>
+<link href="http://www.maptools.org/maptools.css" rel="stylesheet" type="text/css">
</head>
<body>
for reading, writing and updating (to a limited extent) ESRI Shapefiles,
and the associated attribute file (.dbf).<p>
-<h2>Manifest</h2>
+<h2>Supporting Information</h2>
<ul>
-<li> <b>shapelib.html</b>: This file - general documentation on the
-Shapefile C Library.<p>
-
-<li> <b><a href="shp_api.html">shp_api.html</a></b>: Documentation
-for the API for accessing the .shp/.shx files. <p>
-
-<li> <b><a href="dbf_api.html">dbf_api.html</a></b>: Documentation
-for the API for accessing the .dbf attribute files. <p>
-
-<li> <b>shpopen.c</b>: C code for access to .shp/.shx vertex files.<p>
-
-<li> <b>dbfopen.c</b>: C code for access to .dbf attribute file.<p>
-
-<li> <b>shapefil.h</b>: Include file defining all the services of dbfopen.c
-and shpopen.c.<p>
-
-<li> <b>contrib/</b>: A directory of "in progress" contributed programs
-from Carl Anderson.<p>
-
-<li> <b>dbfcreate.c</b>: Simple example program for creating a new .dbf file.
- <p>
-
-<li> <b>dbfadd.c</b>:
- Simple example program for adding a record to a .dbf file.<p>
-
-<li> <b>dbfdump.c</b>: Simple example program for displaying the contents of
- a .dbf file.<p>
-
-<li> <b>shpcreate.c</b>: Simple example program for creating a new .shp and
-.shx file.<p>
-
-<li> <b>shpadd.c</b>: Simple example program for adding a shape to an existing
- shape file.<p>
-
-<li> <b>shpdump.c</b>: Simple program for dumping all the vertices in a
- shapefile with an indicating of the parts.<p>
-
-<li> <b>shputils.c</b>: Complex contributed program capable of clipping and
- appending
- shapefiles as well as a few other things. Type shputils
- after building to get a full usage message.<p>
-
-<li> <b>Makefile</b>: A simple makefile to compile the library and example
- programs.<p>
-
-<li> <b>makeshape.sh</b>: A simple script for running some of the example
-programs.<p>
-
-<li> <b>shptest.c</b>: A simple test harnass to generate each of the supported
- types of shapefiles. <p>
-
-
-<li> <b>shptree.c</b>: Implements a simple quadtree algorithm for fast
-spatial searches of shapefiles.<p>
-
-<li> <b>shptreedump.c</b>: A simple mainly showing information on quad
-trees build using the quad tree api.<p>
-
-<li> <b>stream1.sh</b> - A test script, which should produce stream1.out.
-Note this will only work if you have the example data downloaded.<p>
-
-<li> <b>stream1.out</b>: Expected output of stream1.sh test script.<p>
-
-<li> <b>stream2.sh</b>: A test script, which should produce stream2.out.<p>
-
-<li> <b>stream2.out</b>: Expected output of stream2.sh test script.<p>
-
-<li> <b>pyshapelib-0.1</b>: Prototype contributed Python bindings.<p>
-
+<li> <a href="shp_api.html">Shapefile API Docs</a>
+<li> <a href="dbf_api.html">DBF/xBase API Docs</a>
+<li> <a href="shapelib-tools.html">Shapefile Tools Docs</a>
+<li> <a href="release.html">Release Notes</a>
+<li> <a href="manifest.html">Shapelib File Manifest</a>
+<li> <a href="license.html">Shapelib Licensing Terms</a>
</ul>
<h2>What is a Shapefile?</h2>
If you don't know, you probably don't need this library. The Shapefile
-format is a new working and interchange format promulagated by ESRI
-(http://www.esri.com/) for simple vector data with attributes. It is
-apparently the only file format that can be edited in ARCView 2/3, and can
-also be exported and imported in Arc/Info. <p>
+format is a working and interchange format promulagated by
+<a href="http://www.esri.com/">ESRI</a> for simple vector data with attributes.
+<p>
-An excellent white paper on the shapefile format is available from ESRI,
+An excellent <a href="dl/shapefile.pdf">white paper</a> on the shapefile format
+is available from ESRI,
but it is .pdf format, so you will need Adobe Acrobat to browse it.<p>
The file format actually consists of three files.<p>
XXX.dbf - holds the attributes in xBase (dBase) format.
</pre>
-<h2>Release Notes</h2>
-
-To get notification of new releases of Shapelib <i>subscribe</i> to
-the project at www.freshmeat.net. This is currently the only reliable
-way of finding out about new releases since there is no shapelib specific
-mailing list.<p>
-
-<b>Release 1.2.10</b>: Added SHPRewindObject() function, and shprewind utility
-program. Added FTLogical, DBFReadLogicalAttribute() and
-DBFWriteLogicalAttribute() (thanks to Olek Neyman). <p>
-
-<b>Release 1.2.9</b>: Good support for reading and writing NULL fields
-in .dbf files, good support for NULL shapes and addition of the
-DBFGetFieldIndex() functions (all contributed by Jim Matthews).<p>
-
-An upgraded shputils.c has been contributed by Bill Miller. Daniel
-Morissette contributed DBFGetNativeFieldType(). Better error checking
-for disk errors in dbfopen.c. Various other bug fixes and safety improvements.
-<p>
-
-<b>Release 1.2.8</b>: Added hacked libtool support (supplied by Jan)
-and "rpm ready" install logic.<p>
-
-<b>Release 1.2.7</b>: Fix record size (was 4 bytes too long). Modify
-SHPReadObject() to handle null shapes properly. Use atof() instead of
-sscanf(). Support .DBF as well as .dbf.<p>
-
-<b>Release 1.2.6</b>: Now available under old MIT style license, or at the
-users option, LGPL. Added the contrib directory of stuff from Carl Anderson
-and the shptree.c API for quadtree based spatial searches.<p>
+<h2>Download</h2>
-<b>Release 1.2.5</b>: SHPOpen() now forcably uses "rb" or "r+b" access string
-to avoid common mistakes on Windows. Also fixed a serious bug with .dbf
-files with a 'F' field type.<p>
+Source code, and some other odds and ends can be downloaded from
+<a href="http://download.osgeo.org/shapelib">http://download.osgeo.org/shapelib</a> or <a href="http://shapelib.maptools.org/dl">http://shapelib.maptools.org/dl</a>.<p>
-<b>Release 1.2.4</b>: DBFOpen() will now automatically translate a .shp
-extension to .dbf for convenience. SHPOpen() will try datasets with lower
-and uppercase extension. DBFAddField() now returns the field number,
-not TRUE/FALSE.<p>
-
-<b>Release 1.2.3</b>: Disable writing measures to multi-patches as ArcView
-seems to puke on them (as reported by Monika Sester). Add white space
-trimming, and string/numeric attribute interchangability in DBF API
-as suggested by Steve Lime. Dbfdump was updated to include several
-reporting options.<p>
-
-<b>Release 1.2.2</b>: Added proper support for multipatch (reading and
-writing) - this release just for testing purposes.<p>
-
-<b>Release 1.2</b> is mostly a rewrite of the .shp/.shx access API to account
-for ArcView 3.x 3D shapes, and to encapsulate the shapes in a structure.
-Existing code using the shapefile library will require substantial changes
-to use release 1.2.<p>
-
-<b>Release V1.1</b> has been built on a number of platforms, and used by a
-number of people successfully. V1.1 is the first release with the xBase API
-documentation.<p>
+Shapelib is available for anonymous CVS access:
+<pre>
+ cvs -d :pserver:cvsanon@cvs.maptools.org:/cvs/maptools/cvsroot login
+ Password: (hit enter)
+ cvs -d :pserver:cvsanon@cvs.maptools.org:/cvs/maptools/cvsroot co shapelib
+</pre>
-<h2>Maintainer</h2>
+<h2>Bugs, Maintainance and Support</h2>
-This library is maintained by me (Frank Warmerdam) on my own time. Please
-send me bug patches and suggestions for the library. Email can be sent to
-warmerdam@pobox.com.<p>
+This library is maintained by <a href="http://pobox.com/~warmerdam">Frank
+Warmerdam</a>. Please send me bug reports, patches and suggestions for the
+library via the <a href="http://bugzilla.maptools.org/enter_bug.cgi?product=Shapelib">maptools.org Bugzilla</a>. Shapelib bugs can also be
+<a href="http://bugzilla.maptools.org/query.cgi?product=Shapelib">queried</a>.
+<p>
-The current status of the Shapelib code can be found at
-<a href="http://pobox.com/~warmerdam/root/projects/shapelib/">
-http://pobox.com/~warmerdam/root/projects/shapelib/</a>. To find out about
-new releases of Shapelib, select the "Subscribe to new releases" option
-from the link at
+Shapelib is hosted at
+<a href="http://shapelib.maptools.org">shapelib.maptools.org</a>. A mailing
+list for discussion of how to use shapelib, and announcing new releases
+<a href="http://lists.maptools.org/mailman/listinfo/shapelib/">is
+available</a>. To only find out about new releases of Shapelib select the
+"<i>Subscribe to new releases</i>" option from the link at
<a href="http://freshmeat.net/projects/shapelib/">Freshmeat</a>.<p>
-The shputils.c module was contributed by Bill Miller (NC-DOT) who can be
-reached at bmiller@doh.dot.state.nc.us. I had to modify it substantially
-to work with the 1.2 API, and I am not sure that it works as well as it
-did when it was originally provided by Bill.<p>
-
<h2>Credits</h2>
I didn't start this section anywhere near soon enough, so alot of earlier
</ul>
-<h2>Copyright</h2>
-
-The source for the Shapefile C Library is (c) 1998 Frank Warmerdam,
-and released under the following conditions. The intent is that anyone
-can do anything with the code, but that I do not assume any liability, nor
-express any warranty for this code. <p>
-
-As of Shapelib 1.2.6 the core portions of the library are made available
-under two possible licenses. The licensee can choose to use the code
-under either the Library GNU Public License (LGPL) described in
-LICENSE.LGPL or under the following MIT style license. Any files in
-the Shapelib distribution without explicit copyright license terms
-(such as this documentation, the Makefile and so forth) should be
-considered to have the following licensing terms. Some auxilary portions
-of Shapelib, notably some of the components in the contrib directory
-come under slightly different license restrictions. Check the source
-files that you are actually using for conditions.<p>
-
-<h3>Default License Terms</h3>
-
-<quote>
-Copyright (c) 1999, Frank Warmerdam<p>
-
-This software is available under the following "MIT Style" license,
-or at the option of the licensee under the LGPL (see LICENSE.LGPL). This
-option is discussed in more detail in shapelib.html.<p>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:<p>
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.<p>
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.<p>
-</quote>
-
-<h3>Shapelib Modifications</h3>
-
-I am pleased to receive bug fixes, and improvements for Shapelib. Unless
-the submissions indicate otherwise I will assume that changes submitted to
-me remain under the the above "dual license" terms. If changes are made
-to the library with the intention that those changes should be protected by
-the LGPL then I should be informed upon submission. Note that I will not
-generally incorporate changes into the core of Shapelib that are protected
-under the LGPL as this would effectively limit the whole file and
-distribution to LGPL terms.<p>
-
-<h3>Opting for LGPL</h3>
-
-For licensee's opting to use Shapelib under LGPL as opposed to the MIT
-Style license above, and wishing to redistribute the software based on
-Shapelib, I would ask that all "dual license" modules be updated to
-indicate that only the LGPL (and not the MIT Style license) applies. This
-action represents opting for the LGPL, and thereafter LGPL terms apply to
-any redistribution and modification of the affected modules.<p>
+<h2>Other Shapefile Resources</h2>
+
+<ul>
+<li> <a href="dl/shapefile.pdf">Shapefile Format Specifications (pdf)</a><p>
+
+<li> <a href="http://www.clicketyclick.dk/databases/xbase/format/">Xbase (.dbf) File Format Description</a>. <p>
+
+<li> <a href="codepage.html">Language ID / Code Page mappings</a><p>
+
+<li> Shapelib is used within the multiformat
+<a href="http://ogr.maptools.org/">OGR</a> library. If you are looking for a
+high level C++ library with support for many geospatial vector formats you
+might want to check it out.<p>
+
+<li> Ari Jolma has produced an initial <b>perl</b> binding on top of shapelib,
+which can be found at CPAN as Geo::ShapeFile under the
+<a href="http://www.cpan.org/modules/by-module/Geo/">Geo</a> module.
+<p>
+
+<li> Bernhard Herzog has produced <b>python</b> bindings for Shapelib with
+SWIG, available at <a href="http://ftp.intevation.de/users/bh/pyshapelib/">http://ftp.intevation.de/users/bh/pyshapelib</a>. A new version not using swig is
+available as <a href="http://wald.intevation.org/plugins/scmsvn/viewcvs.php/trunk/thuban/libraries/pyshapelib/?root=thuban">part of Thuban</a>.<p>
+
+<li> <a href="http://www.triplexware.huckfinn.de/shpapi.html">Delphi</a>
+bindings for Shapelib courtesy of Alexander Weidauer.<p>
+
+<li> Miguel Filgueiras has implemented
+<a href="http://www.ncc.up.pt/gpsmanshp/">Tcl</a> bindings for Shapelib
+as part of <a href="http://www.ncc.up.pt/gpsman/">GPSMan</a>.<p>
+
+<li> David Gancarz has implemented a Microsoft
+<a href="dl/contrib/DotNetArchive.zip">.NET wrapper</a> for
+Shapelib. An example of using shapelib with VB6 is also icluded in the .NET wrapper project file.<p>
+
+<li> Andrey Hristov (php at hristov dot com) has developed a PHP extension
+based on Shapelib. It can be found in CVS at http://cvs.php.net/pecl/shp.<p>
+
+<li> Toyoda Eizi has developed Ruby bindings found at
+<a href="http://sourceforge.net/projects/ruby-shapelib">http://sourceforge.net/projects/ruby-shapelib</a>.<p>
+
+<li> Davide Cesari has developed FORTRAN bindings that can be found at
+<a href"http://www.webalice.it/o.drofa/davide/shapelib-fortran/">
+http://www.webalice.it/o.drofa/davide/shapelib-fortran</a>.
+
+<li> Jan-Oliver Wagner has implemented a commandline program
+(<b>gen2shp</b>) for producing shapefiles from Arc/Info Generate format ASCII
+files. He maintains a <a href="http://intevation.de/~jan/gen2shp">web page</a> for his work. <p>
+
+<li> Tom Russo has implemented a shpcs2cs program, which reprojects shapefiles
+using arguments similar to the PROJ.4 cs2cs program including datum conversion.
+Use as an alternate to the contrib/shpproj which doesn't do datums. It is
+available at the bottom of Tom's <a href="http://www.swcp.com/~russo/shape_web/">Xastir Shapefile Resources</a> page. <p>
+
+<li>
+Andrew Williamson's
+<a href="http://www.geocities.com/SiliconValley/Haven/2295/useful.html">Useful
+Scripts and Stuff</a> page for ArcView, which includes ShapeChecker.<p>
+
+<li> The University of Bonn <a href="http://katla.giub.uni-bonn.de/sfjava/">
+sf4java</a> project apparently includes Java classes for reading Shapefiles.<p>
+
+<li> The <a href="http://gis.esri.com/arcscripts/details.cfm?CFGRIDKEY=628102085">ShapeIO2</a> Visual Basic libraries may be of interest to those wanting
+VB access to Shapefiles. Also available <a href="http://shapelib.maptools.org/dl/contrib/ShapeIO2.zip">locally</a>.<p>
+
+<li> The <a href="http://arcscripts.esri.com/details.asp?dbid=11810">ShapeFile Read/Write OCX</a> is another option for Visual Basic programmers.<p>
+
+<li> <a href="http://www.casa.ucl.ac.uk/sanjay/software_isovistanalyst.htm">Isovist Analyst</a> is a sort-of-free isovist generating extension for
+ArcView using shapelib.<p>
+
+<li> <a href="http://www.obviously.com/gis/shpdiff/">shpdiff</a> utility
+by Bryce Nesbitt.<p>
+
+<li> <a href="http://www.aequometer.de/">Aequometer</a>: a program for
+MS Excel to calculate the area of polygons and export as shapefiles.<p>
+
+</ul>
</body>
</html>
/******************************************************************************
- * $Id: shpopen.c,v 1.5 2006-11-24 21:55:52 oliskoli Exp $
+ * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $
*
* Project: Shapelib
* Purpose: Implementation of core Shapefile read/write functions.
* DEALINGS IN THE SOFTWARE.
******************************************************************************
*
- * $Log: not supported by cvs2svn $
- * Revision 1.4 2006/07/13 03:27:54 robertl
- * Andy Armstrong turns on -Wall for GCC builds and kills about a sequillion warnings. Most of them aren't "real", but it's still a good thing to clean up.
- * (I hope I don't regret this before 1.3.1...)
+ * $Log: shpopen.c,v $
+ * Revision 1.73 2012-01-24 22:33:01 fwarmerdam
+ * fix memory leak on failure to open .shp (gdal #4410)
*
- * Revision 1.3 2006/05/07 02:14:35 robertl
- * Make shapefile and all palm pdb formats deselectable at build time.
+ * Revision 1.72 2011-12-11 22:45:28 fwarmerdam
+ * fix failure return from SHPOpenLL.
*
- * Revision 1.2 2004/09/27 01:13:58 robertl
- * warning fixes in shapelib. From Alexander Stohr.
+ * Revision 1.71 2011-09-15 03:33:58 fwarmerdam
+ * fix missing cast (#2344)
*
- * Revision 1.1 2004/09/20 17:21:22 robertl
- * Check in shapelib and experimental prototype of crude shapefile support.
+ * Revision 1.70 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
+ *
+ * Revision 1.69 2011-07-24 03:24:22 fwarmerdam
+ * fix memory leaks in error cases creating shapefiles (#2061)
+ *
+ * Revision 1.68 2010-08-27 23:42:52 fwarmerdam
+ * add SHPAPI_CALL attribute in code
+ *
+ * Revision 1.67 2010-07-01 08:15:48 fwarmerdam
+ * do not error out on an object with zero vertices
+ *
+ * Revision 1.66 2010-07-01 07:58:57 fwarmerdam
+ * minor cleanup of error handling
+ *
+ * Revision 1.65 2010-07-01 07:27:13 fwarmerdam
+ * white space formatting adjustments
+ *
+ * Revision 1.64 2010-01-28 11:34:34 fwarmerdam
+ * handle the shape file length limits more gracefully (#3236)
+ *
+ * Revision 1.63 2010-01-28 04:04:40 fwarmerdam
+ * improve numerical accuracy of SHPRewind() algs (gdal #3363)
+ *
+ * Revision 1.62 2010-01-17 05:34:13 fwarmerdam
+ * Remove asserts on x/y being null (#2148).
+ *
+ * Revision 1.61 2010-01-16 05:07:42 fwarmerdam
+ * allow 0/nulls in shpcreateobject (#2148)
+ *
+ * Revision 1.60 2009-09-17 20:50:02 bram
+ * on Win32, define snprintf as alias to _snprintf
+ *
+ * Revision 1.59 2008-03-14 05:25:31 fwarmerdam
+ * Correct crash on buggy geometries (gdal #2218)
+ *
+ * Revision 1.58 2008/01/08 23:28:26 bram
+ * on line 2095, use a float instead of a double to avoid a compiler warning
+ *
+ * Revision 1.57 2007/12/06 07:00:25 fwarmerdam
+ * dbfopen now using SAHooks for fileio
+ *
+ * Revision 1.56 2007/12/04 20:37:56 fwarmerdam
+ * preliminary implementation of hooks api for io and errors
+ *
+ * Revision 1.55 2007/11/21 22:39:56 fwarmerdam
+ * close shx file in readonly mode (GDAL #1956)
+ *
+ * Revision 1.54 2007/11/15 00:12:47 mloskot
+ * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
+ *
+ * Revision 1.53 2007/11/14 22:31:08 fwarmerdam
+ * checks after mallocs to detect for corrupted/voluntary broken shapefiles.
+ * http://trac.osgeo.org/gdal/ticket/1991
+ *
+ * Revision 1.52 2007/06/21 15:58:33 fwarmerdam
+ * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
+ *
+ * Revision 1.51 2006/09/04 15:24:01 fwarmerdam
+ * Fixed up log message for 1.49.
+ *
+ * Revision 1.50 2006/09/04 15:21:39 fwarmerdam
+ * fix of last fix
+ *
+ * Revision 1.49 2006/09/04 15:21:00 fwarmerdam
+ * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated
+ * files. The problem was discovered by Tim Sutton and reported here
+ * https://svn.qgis.org/trac/ticket/200
+ *
+ * Revision 1.48 2006/01/26 15:07:32 fwarmerdam
+ * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
+ *
+ * Revision 1.47 2006/01/04 20:07:23 fwarmerdam
+ * In SHPWriteObject() make sure that the record length is updated
+ * when rewriting an existing record.
+ *
+ * Revision 1.46 2005/02/11 17:17:46 fwarmerdam
+ * added panPartStart[0] validation
+ *
+ * Revision 1.45 2004/09/26 20:09:48 fwarmerdam
+ * const correctness changes
+ *
+ * Revision 1.44 2003/12/29 00:18:39 fwarmerdam
+ * added error checking for failed IO and optional CPL error reporting
+ *
+ * Revision 1.43 2003/12/01 16:20:08 warmerda
+ * be careful of zero vertex shapes
+ *
+ * Revision 1.42 2003/12/01 14:58:27 warmerda
+ * added degenerate object check in SHPRewindObject()
+ *
+ * Revision 1.41 2003/07/08 15:22:43 warmerda
+ * avoid warning
+ *
+ * Revision 1.40 2003/04/21 18:30:37 warmerda
+ * added header write/update public methods
*
* Revision 1.39 2002/08/26 06:46:56 warmerda
* avoid c++ comments
*
*/
-/*static char rcsid[] =
- "$Id: shpopen.c,v 1.5 2006-11-24 21:55:52 oliskoli Exp $";*/
-
#include "shapefil.h"
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-#if SHAPELIB_ENABLED
#include <math.h>
#include <limits.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+
+SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $")
typedef unsigned char uchar;
#if UINT_MAX == 65535
-typedef long int32;
+typedef unsigned long int32;
#else
-typedef int int32;
+typedef unsigned int int32;
#endif
#ifndef FALSE
# define MAX(a,b) ((a>b) ? a : b)
#endif
+#if defined(WIN32) || defined(_WIN32)
+# ifndef snprintf
+# define snprintf _snprintf
+# endif
+#endif
+
static int bBigEndian;
/* contents of the index (.shx) file. */
/************************************************************************/
-static void SHPWriteHeader( SHPHandle psSHP )
+void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP )
{
uchar abyHeader[100];
int32 i32;
double dValue;
int32 *panSHX;
+
+ if (psSHP->fpSHX == NULL)
+ {
+ psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
+ return;
+ }
/* -------------------------------------------------------------------- */
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 100; i++ )
- abyHeader[i] = 0;
+ abyHeader[i] = 0;
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
/* -------------------------------------------------------------------- */
/* Write .shp file header. */
/* -------------------------------------------------------------------- */
- fseek( psSHP->fpSHP, 0, 0 );
- fwrite( abyHeader, 100, 1, psSHP->fpSHP );
+ if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0
+ || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 )
+ {
+ psSHP->sHooks.Error( "Failure writing .shp header" );
+ return;
+ }
/* -------------------------------------------------------------------- */
/* Prepare, and write .shx file header. */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
- fseek( psSHP->fpSHX, 0, 0 );
- fwrite( abyHeader, 100, 1, psSHP->fpSHX );
+ if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0
+ || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
+ {
+ psSHP->sHooks.Error( "Failure writing .shx header" );
+ return;
+ }
/* -------------------------------------------------------------------- */
/* Write out the .shx contents. */
for( i = 0; i < psSHP->nRecords; i++ )
{
- panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
- panSHX[i*2+1] = psSHP->panRecSize[i]/2;
- if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
- if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
+ panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
+ panSHX[i*2+1] = psSHP->panRecSize[i]/2;
+ if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
+ if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
}
- fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX );
+ if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX )
+ != psSHP->nRecords )
+ {
+ psSHP->sHooks.Error( "Failure writing .shx contents" );
+ }
free( panSHX );
+
+/* -------------------------------------------------------------------- */
+/* Flush to disk. */
+/* -------------------------------------------------------------------- */
+ psSHP->sHooks.FFlush( psSHP->fpSHP );
+ psSHP->sHooks.FFlush( psSHP->fpSHX );
+}
+
+/************************************************************************/
+/* SHPOpen() */
+/************************************************************************/
+
+SHPHandle SHPAPI_CALL
+SHPOpen( const char * pszLayer, const char * pszAccess )
+
+{
+ SAHooks sHooks;
+
+ SASetupDefaultHooks( &sHooks );
+
+ return SHPOpenLL( pszLayer, pszAccess, &sHooks );
}
/************************************************************************/
/************************************************************************/
SHPHandle SHPAPI_CALL
-SHPOpen( const char * pszLayer, const char * pszAccess )
+SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
{
char *pszFullname, *pszBasename;
psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
psSHP->bUpdated = FALSE;
+ memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
/* -------------------------------------------------------------------- */
/* Compute the base (layer) name. If there is any extension */
pszBasename = (char *) malloc(strlen(pszLayer)+5);
strcpy( pszBasename, pszLayer );
for( i = strlen(pszBasename)-1;
- i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
- && pszBasename[i] != '\\';
- i-- ) {}
+ i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+ && pszBasename[i] != '\\';
+ i-- ) {}
if( pszBasename[i] == '.' )
pszBasename[i] = '\0';
/* a PC to Unix with upper case filenames won't work! */
/* -------------------------------------------------------------------- */
pszFullname = (char *) malloc(strlen(pszBasename) + 5);
- sprintf( pszFullname, "%s.shp", pszBasename );
- psSHP->fpSHP = fopen(pszFullname, pszAccess );
+ sprintf( pszFullname, "%s.shp", pszBasename ) ;
+ psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
if( psSHP->fpSHP == NULL )
{
sprintf( pszFullname, "%s.SHP", pszBasename );
- psSHP->fpSHP = fopen(pszFullname, pszAccess );
+ psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
}
if( psSHP->fpSHP == NULL )
{
+ char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
+ sprintf( pszMessage, "Unable to open %s.shp or %s.SHP.",
+ pszBasename, pszBasename );
+ psHooks->Error( pszMessage );
+ free( pszMessage );
+
free( psSHP );
free( pszBasename );
free( pszFullname );
- return( NULL );
+
+ return NULL;
}
sprintf( pszFullname, "%s.shx", pszBasename );
- psSHP->fpSHX = fopen(pszFullname, pszAccess );
+ psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
if( psSHP->fpSHX == NULL )
{
sprintf( pszFullname, "%s.SHX", pszBasename );
- psSHP->fpSHX = fopen(pszFullname, pszAccess );
+ psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
}
if( psSHP->fpSHX == NULL )
{
- fclose( psSHP->fpSHP );
+ char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
+ sprintf( pszMessage, "Unable to open %s.shx or %s.SHX.",
+ pszBasename, pszBasename );
+ psHooks->Error( pszMessage );
+ free( pszMessage );
+
+ psSHP->sHooks.FClose( psSHP->fpSHP );
free( psSHP );
free( pszBasename );
free( pszFullname );
/* Read the file size from the SHP file. */
/* -------------------------------------------------------------------- */
pabyBuf = (uchar *) malloc(100);
- fread( pabyBuf, 100, 1, psSHP->fpSHP );
+ psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP );
- psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
- + pabyBuf[25] * 256 * 256
- + pabyBuf[26] * 256
- + pabyBuf[27]) * 2;
+ psSHP->nFileSize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256
+ + (unsigned int)pabyBuf[25] * 256 * 256
+ + (unsigned int)pabyBuf[26] * 256
+ + (unsigned int)pabyBuf[27]) * 2;
/* -------------------------------------------------------------------- */
/* Read SHX file Header info */
/* -------------------------------------------------------------------- */
- fread( pabyBuf, 100, 1, psSHP->fpSHX );
-
- if( pabyBuf[0] != 0
+ if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1
+ || pabyBuf[0] != 0
|| pabyBuf[1] != 0
|| pabyBuf[2] != 0x27
|| (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
{
- fclose( psSHP->fpSHP );
- fclose( psSHP->fpSHX );
- free( psSHP );
+ psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
+ psSHP->sHooks.FClose( psSHP->fpSHP );
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ free( psSHP );
- return( NULL );
+ return( NULL );
}
psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256
- + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
+ + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
psSHP->nShapeType = pabyBuf[32];
if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
{
- /* this header appears to be corrupt. Give up. */
- fclose( psSHP->fpSHP );
- fclose( psSHP->fpSHX );
- free( psSHP );
+ char szError[200];
+
+ sprintf( szError,
+ "Record count in .shp header is %d, which seems\n"
+ "unreasonable. Assuming header is corrupt.",
+ psSHP->nRecords );
+ psSHP->sHooks.Error( szError );
+ psSHP->sHooks.FClose( psSHP->fpSHP );
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ free( psSHP );
+ free(pabyBuf);
- return( NULL );
+ return( NULL );
}
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
psSHP->nMaxRecords = psSHP->nRecords;
- psSHP->panRecOffset =
- (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
- psSHP->panRecSize =
- (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
-
+ psSHP->panRecOffset = (unsigned int *)
+ malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
+ psSHP->panRecSize = (unsigned int *)
+ malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
- fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX );
+
+ if (psSHP->panRecOffset == NULL ||
+ psSHP->panRecSize == NULL ||
+ pabyBuf == NULL)
+ {
+ char szError[200];
+
+ sprintf(szError,
+ "Not enough memory to allocate requested memory (nRecords=%d).\n"
+ "Probably broken SHP file",
+ psSHP->nRecords );
+ psSHP->sHooks.Error( szError );
+ psSHP->sHooks.FClose( psSHP->fpSHP );
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ if (psSHP->panRecOffset) free( psSHP->panRecOffset );
+ if (psSHP->panRecSize) free( psSHP->panRecSize );
+ if (pabyBuf) free( pabyBuf );
+ free( psSHP );
+ return( NULL );
+ }
+
+ if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX )
+ != psSHP->nRecords )
+ {
+ char szError[200];
+
+ sprintf( szError,
+ "Failed to read all values for %d records in .shx file.",
+ psSHP->nRecords );
+ psSHP->sHooks.Error( szError );
+
+ /* SHX is short or unreadable for some reason. */
+ psSHP->sHooks.FClose( psSHP->fpSHP );
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ free( psSHP->panRecOffset );
+ free( psSHP->panRecSize );
+ free( pabyBuf );
+ free( psSHP );
+
+ return( NULL );
+ }
+
+ /* In read-only mode, we can close the SHX now */
+ if (strcmp(pszAccess, "rb") == 0)
+ {
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ psSHP->fpSHX = NULL;
+ }
for( i = 0; i < psSHP->nRecords; i++ )
{
- int32 nOffset, nLength;
+ int32 nOffset, nLength;
- memcpy( &nOffset, pabyBuf + i * 8, 4 );
- if( !bBigEndian ) SwapWord( 4, &nOffset );
+ memcpy( &nOffset, pabyBuf + i * 8, 4 );
+ if( !bBigEndian ) SwapWord( 4, &nOffset );
- memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
- if( !bBigEndian ) SwapWord( 4, &nLength );
+ memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
+ if( !bBigEndian ) SwapWord( 4, &nLength );
- psSHP->panRecOffset[i] = nOffset*2;
- psSHP->panRecSize[i] = nLength*2;
+ psSHP->panRecOffset[i] = nOffset*2;
+ psSHP->panRecSize[i] = nLength*2;
}
free( pabyBuf );
SHPClose(SHPHandle psSHP )
{
+ if( psSHP == NULL )
+ return;
+
/* -------------------------------------------------------------------- */
/* Update the header if we have modified anything. */
/* -------------------------------------------------------------------- */
if( psSHP->bUpdated )
- {
SHPWriteHeader( psSHP );
- }
/* -------------------------------------------------------------------- */
/* Free all resources, and close files. */
free( psSHP->panRecOffset );
free( psSHP->panRecSize );
- fclose( psSHP->fpSHX );
- fclose( psSHP->fpSHP );
+ if ( psSHP->fpSHX != NULL)
+ psSHP->sHooks.FClose( psSHP->fpSHX );
+ psSHP->sHooks.FClose( psSHP->fpSHP );
if( psSHP->pabyRec != NULL )
{
{
int i;
+
+ if( psSHP == NULL )
+ return;
if( pnEntities != NULL )
*pnEntities = psSHP->nRecords;
SHPCreate( const char * pszLayer, int nShapeType )
{
- char *pszBasename, *pszFullname;
+ SAHooks sHooks;
+
+ SASetupDefaultHooks( &sHooks );
+
+ return SHPCreateLL( pszLayer, nShapeType, &sHooks );
+}
+
+/************************************************************************/
+/* SHPCreate() */
+/* */
+/* Create a new shape file and return a handle to the open */
+/* shape file with read/write access. */
+/************************************************************************/
+
+SHPHandle SHPAPI_CALL
+SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
+
+{
+ char *pszBasename = NULL, *pszFullname = NULL;
int i;
- FILE *fpSHP, *fpSHX;
+ SAFile fpSHP = NULL, fpSHX = NULL;
uchar abyHeader[100];
int32 i32;
double dValue;
pszBasename = (char *) malloc(strlen(pszLayer)+5);
strcpy( pszBasename, pszLayer );
for( i = strlen(pszBasename)-1;
- i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
- && pszBasename[i] != '\\';
- i-- ) {}
+ i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+ && pszBasename[i] != '\\';
+ i-- ) {}
if( pszBasename[i] == '.' )
pszBasename[i] = '\0';
/* -------------------------------------------------------------------- */
pszFullname = (char *) malloc(strlen(pszBasename) + 5);
sprintf( pszFullname, "%s.shp", pszBasename );
- fpSHP = fopen(pszFullname, "wb" );
+ fpSHP = psHooks->FOpen(pszFullname, "wb" );
if( fpSHP == NULL )
- return( NULL );
+ {
+ psHooks->Error( "Failed to create file .shp file." );
+ goto error;
+ }
sprintf( pszFullname, "%s.shx", pszBasename );
- fpSHX = fopen(pszFullname, "wb" );
+ fpSHX = psHooks->FOpen(pszFullname, "wb" );
if( fpSHX == NULL )
- return( NULL );
+ {
+ psHooks->Error( "Failed to create file .shx file." );
+ goto error;
+ }
- free( pszFullname );
- free( pszBasename );
+ free( pszFullname ); pszFullname = NULL;
+ free( pszBasename ); pszBasename = NULL;
/* -------------------------------------------------------------------- */
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 100; i++ )
- abyHeader[i] = 0;
+ abyHeader[i] = 0;
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
/* -------------------------------------------------------------------- */
/* Write .shp file header. */
/* -------------------------------------------------------------------- */
- fwrite( abyHeader, 100, 1, fpSHP );
+ if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
+ {
+ psHooks->Error( "Failed to write .shp header." );
+ goto error;
+ }
/* -------------------------------------------------------------------- */
/* Prepare, and write .shx file header. */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
- fwrite( abyHeader, 100, 1, fpSHX );
+ if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
+ {
+ psHooks->Error( "Failed to write .shx header." );
+ goto error;
+ }
/* -------------------------------------------------------------------- */
/* Close the files, and then open them as regular existing files. */
/* -------------------------------------------------------------------- */
- fclose( fpSHP );
- fclose( fpSHX );
+ psHooks->FClose( fpSHP );
+ psHooks->FClose( fpSHX );
- return( SHPOpen( pszLayer, "r+b" ) );
+ return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
+
+error:
+ if (pszFullname) free(pszFullname);
+ if (pszBasename) free(pszBasename);
+ if (fpSHP) psHooks->FClose( fpSHP );
+ if (fpSHX) psHooks->FClose( fpSHX );
+ return NULL;
}
/************************************************************************/
SHPObject SHPAPI_CALL1(*)
SHPCreateObject( int nSHPType, int nShapeId, int nParts,
- int * panPartStart, int * panPartType,
- int nVertices, const double * padfX, const double * padfY,
+ const int * panPartStart, const int * panPartType,
+ int nVertices, const double *padfX, const double *padfY,
const double * padfZ, const double * padfM )
{
psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
psObject->nSHPType = nSHPType;
psObject->nShapeId = nShapeId;
+ psObject->bMeasureIsUsed = FALSE;
/* -------------------------------------------------------------------- */
/* Establish whether this shape type has M, and Z values. */
psObject->nParts = MAX(1,nParts);
psObject->panPartStart = (int *)
- malloc(sizeof(int) * psObject->nParts);
+ calloc(sizeof(int), psObject->nParts);
psObject->panPartType = (int *)
malloc(sizeof(int) * psObject->nParts);
for( i = 0; i < nParts; i++ )
{
- psObject->panPartStart[i] = panPartStart[i];
+ if( psObject->panPartStart != NULL )
+ psObject->panPartStart[i] = panPartStart[i];
+
if( panPartType != NULL )
psObject->panPartType[i] = panPartType[i];
else
psObject->panPartType[i] = SHPP_RING;
}
+
+ if( psObject->panPartStart[0] != 0 )
+ psObject->panPartStart[0] = 0;
}
/* -------------------------------------------------------------------- */
-/* Capture vertices. Note that Z and M are optional, but X and */
-/* Y are not. */
+/* Capture vertices. Note that X, Y, Z and M are optional. */
/* -------------------------------------------------------------------- */
if( nVertices > 0 )
{
psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
psObject->padfM = (double *) calloc(sizeof(double),nVertices);
- assert( padfX != NULL );
- assert( padfY != NULL );
-
for( i = 0; i < nVertices; i++ )
{
- psObject->padfX[i] = padfX[i];
- psObject->padfY[i] = padfY[i];
+ if( padfX != NULL )
+ psObject->padfX[i] = padfX[i];
+ if( padfY != NULL )
+ psObject->padfY[i] = padfY[i];
if( padfZ != NULL && bHasZ )
psObject->padfZ[i] = padfZ[i];
if( padfM != NULL && bHasM )
psObject->padfM[i] = padfM[i];
}
+ if( padfM != NULL && bHasM )
+ psObject->bMeasureIsUsed = TRUE;
}
/* -------------------------------------------------------------------- */
SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
{
- int nRecordOffset, i, nRecordSize;
+ unsigned int nRecordOffset, nRecordSize=0;
+ int i;
uchar *pabyRec;
int32 i32;
- nRecordSize = 0;
psSHP->bUpdated = TRUE;
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
{
- psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
+ psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
- psSHP->panRecOffset = (int *)
- SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
- psSHP->panRecSize = (int *)
- SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
+ psSHP->panRecOffset = (unsigned int *)
+ SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * psSHP->nMaxRecords );
+ psSHP->panRecSize = (unsigned int *)
+ SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * psSHP->nMaxRecords );
}
/* -------------------------------------------------------------------- */
/* Initialize record. */
/* -------------------------------------------------------------------- */
pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double)
- + psObject->nParts * 8 + 128);
+ + psObject->nParts * 8 + 128);
/* -------------------------------------------------------------------- */
/* Extract vertices for a Polygon or Arc. */
|| psObject->nSHPType == SHPT_ARCM
|| psObject->nSHPType == SHPT_MULTIPATCH )
{
- int32 nPoints, nParts;
- int i;
+ int32 nPoints, nParts;
+ int i;
- nPoints = psObject->nVertices;
- nParts = psObject->nParts;
+ nPoints = psObject->nVertices;
+ nParts = psObject->nParts;
- _SHPSetBounds( pabyRec + 12, psObject );
+ _SHPSetBounds( pabyRec + 12, psObject );
- if( bBigEndian ) SwapWord( 4, &nPoints );
- if( bBigEndian ) SwapWord( 4, &nParts );
+ if( bBigEndian ) SwapWord( 4, &nPoints );
+ if( bBigEndian ) SwapWord( 4, &nParts );
- ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
- ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
+ ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
+ ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
nRecordSize = 52;
/*
* Write part start positions.
*/
- ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
+ ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
4 * psObject->nParts );
- for( i = 0; i < psObject->nParts; i++ )
- {
- if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
+ for( i = 0; i < psObject->nParts; i++ )
+ {
+ if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
nRecordSize += 4;
- }
+ }
/*
* Write multipatch part types if needed.
/*
* Write the (x,y) vertex values.
*/
- for( i = 0; i < psObject->nVertices; i++ )
- {
- ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
- ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
+ for( i = 0; i < psObject->nVertices; i++ )
+ {
+ ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
+ ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
- if( bBigEndian )
+ if( bBigEndian )
SwapWord( 8, pabyRec + nRecordSize );
- if( bBigEndian )
+ if( bBigEndian )
SwapWord( 8, pabyRec + nRecordSize + 8 );
nRecordSize += 2 * 8;
- }
+ }
/*
* Write the Z coordinates (if any).
/*
* Write the M values, if any.
*/
- if( psObject->nSHPType == SHPT_POLYGONM
- || psObject->nSHPType == SHPT_ARCM
+ if( psObject->bMeasureIsUsed
+ && (psObject->nSHPType == SHPT_POLYGONM
+ || psObject->nSHPType == SHPT_ARCM
#ifndef DISABLE_MULTIPATCH_MEASURE
- || psObject->nSHPType == SHPT_MULTIPATCH
+ || psObject->nSHPType == SHPT_MULTIPATCH
#endif
- || psObject->nSHPType == SHPT_POLYGONZ
- || psObject->nSHPType == SHPT_ARCZ )
+ || psObject->nSHPType == SHPT_POLYGONZ
+ || psObject->nSHPType == SHPT_ARCZ) )
{
ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
|| psObject->nSHPType == SHPT_MULTIPOINTZ
|| psObject->nSHPType == SHPT_MULTIPOINTM )
{
- int32 nPoints;
- int i;
+ int32 nPoints;
+ int i;
- nPoints = psObject->nVertices;
+ nPoints = psObject->nVertices;
_SHPSetBounds( pabyRec + 12, psObject );
- if( bBigEndian ) SwapWord( 4, &nPoints );
- ByteCopy( &nPoints, pabyRec + 44, 4 );
+ if( bBigEndian ) SwapWord( 4, &nPoints );
+ ByteCopy( &nPoints, pabyRec + 44, 4 );
- for( i = 0; i < psObject->nVertices; i++ )
- {
- ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
- ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
+ for( i = 0; i < psObject->nVertices; i++ )
+ {
+ ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
+ ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
- if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
- if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
- }
+ if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
+ if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
+ }
- nRecordSize = 48 + 16 * psObject->nVertices;
+ nRecordSize = 48 + 16 * psObject->nVertices;
if( psObject->nSHPType == SHPT_MULTIPOINTZ )
{
}
}
- if( psObject->nSHPType == SHPT_MULTIPOINTZ
- || psObject->nSHPType == SHPT_MULTIPOINTM )
+ if( psObject->bMeasureIsUsed
+ && (psObject->nSHPType == SHPT_MULTIPOINTZ
+ || psObject->nSHPType == SHPT_MULTIPOINTM) )
{
ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
|| psObject->nSHPType == SHPT_POINTZ
|| psObject->nSHPType == SHPT_POINTM )
{
- ByteCopy( psObject->padfX, pabyRec + 12, 8 );
- ByteCopy( psObject->padfY, pabyRec + 20, 8 );
+ ByteCopy( psObject->padfX, pabyRec + 12, 8 );
+ ByteCopy( psObject->padfY, pabyRec + 20, 8 );
- if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
- if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
+ if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
+ if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
nRecordSize = 28;
nRecordSize += 8;
}
- if( psObject->nSHPType == SHPT_POINTZ
- || psObject->nSHPType == SHPT_POINTM )
+ if( psObject->bMeasureIsUsed
+ && (psObject->nSHPType == SHPT_POINTZ
+ || psObject->nSHPType == SHPT_POINTM) )
{
ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
/* -------------------------------------------------------------------- */
if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
{
+ unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize;
+ if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow
+ {
+ char str[128];
+ sprintf( str, "Failed to write shape object. "
+ "File size cannot reach %u + %u.",
+ psSHP->nFileSize, nRecordSize );
+ psSHP->sHooks.Error( str );
+ free( pabyRec );
+ return -1;
+ }
+
if( nShapeId == -1 )
nShapeId = psSHP->nRecords++;
else
{
nRecordOffset = psSHP->panRecOffset[nShapeId];
+ psSHP->panRecSize[nShapeId] = nRecordSize-8;
}
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
/* Write out record. */
/* -------------------------------------------------------------------- */
- if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0
- || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
+ if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 )
+ {
+ psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." );
+ free( pabyRec );
+ return -1;
+ }
+ if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
{
- printf( "Error in fseek() or fwrite().\n" );
+ psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while writing object to .shp file." );
free( pabyRec );
return -1;
}
if( psSHP->adBoundsMin[0] == 0.0
&& psSHP->adBoundsMax[0] == 0.0
&& psSHP->adBoundsMin[1] == 0.0
- && psSHP->adBoundsMax[1] == 0.0
- && psObject->nSHPType != SHPT_NULL )
+ && psSHP->adBoundsMax[1] == 0.0 )
{
- psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
- psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
- psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
- psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
+ if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
+ {
+ psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
+ psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
+ psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
+ psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
+ }
+ else
+ {
+ psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
+ psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
+ psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
+ psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
+ }
}
for( i = 0; i < psObject->nVertices; i++ )
{
- psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
- psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
- psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
- psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
- psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
- psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
- psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
- psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
+ psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
+ psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
+ psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
+ psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
+ psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
+ psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
+ psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
+ psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
}
return( nShapeId );
SHPReadObject( SHPHandle psSHP, int hEntity )
{
- SHPObject *psShape;
+ int nEntitySize, nRequiredSize;
+ SHPObject *psShape;
+ char szErrorMsg[128];
/* -------------------------------------------------------------------- */
/* Validate the record/entity number. */
/* -------------------------------------------------------------------- */
/* Ensure our record buffer is large enough. */
/* -------------------------------------------------------------------- */
- if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize )
+ nEntitySize = psSHP->panRecSize[hEntity]+8;
+ if( nEntitySize > psSHP->nBufSize )
+ {
+ psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
+ if (psSHP->pabyRec == NULL)
+ {
+ char szError[200];
+
+ /* Reallocate previous successfull size for following features */
+ psSHP->pabyRec = (uchar *) malloc(psSHP->nBufSize);
+
+ sprintf( szError,
+ "Not enough memory to allocate requested memory (nBufSize=%d). "
+ "Probably broken SHP file", psSHP->nBufSize );
+ psSHP->sHooks.Error( szError );
+ return NULL;
+ }
+
+ /* Only set new buffer size after successfull alloc */
+ psSHP->nBufSize = nEntitySize;
+ }
+
+ /* In case we were not able to reallocate the buffer on a previous step */
+ if (psSHP->pabyRec == NULL)
{
- psSHP->nBufSize = psSHP->panRecSize[hEntity]+8;
- psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize);
+ return NULL;
}
/* -------------------------------------------------------------------- */
/* Read the record. */
/* -------------------------------------------------------------------- */
- fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 );
- fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP );
+ if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 )
+ {
+ /*
+ * TODO - mloskot: Consider detailed diagnostics of shape file,
+ * for example to detect if file is truncated.
+ */
+ char str[128];
+ sprintf( str,
+ "Error in fseek() reading object from .shp file at offset %u",
+ psSHP->panRecOffset[hEntity]);
+
+ psSHP->sHooks.Error( str );
+ return NULL;
+ }
+
+ if( psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP ) != 1 )
+ {
+ /*
+ * TODO - mloskot: Consider detailed diagnostics of shape file,
+ * for example to detect if file is truncated.
+ */
+ char str[128];
+ sprintf( str,
+ "Error in fread() reading object of size %u at offset %u from .shp file",
+ nEntitySize, psSHP->panRecOffset[hEntity] );
+
+ psSHP->sHooks.Error( str );
+ return NULL;
+ }
/* -------------------------------------------------------------------- */
/* Allocate and minimally initialize the object. */
/* -------------------------------------------------------------------- */
psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
psShape->nShapeId = hEntity;
+ psShape->bMeasureIsUsed = FALSE;
+ if ( 8 + 4 > nEntitySize )
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
+
if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
/* ==================================================================== */
|| psShape->nSHPType == SHPT_ARCM
|| psShape->nSHPType == SHPT_MULTIPATCH )
{
- int32 nPoints, nParts;
- int i, nOffset;
+ int32_t nPoints, nParts;
+ int i, nOffset;
+ if ( 40 + 8 + 4 > nEntitySize )
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
/* -------------------------------------------------------------------- */
/* Get the X/Y bounds. */
/* -------------------------------------------------------------------- */
memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
/* -------------------------------------------------------------------- */
/* Extract part/point count, and build vertex and part arrays */
/* to proper size. */
/* -------------------------------------------------------------------- */
- memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
- memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
+ memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
+ memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
- if( bBigEndian ) SwapWord( 4, &nPoints );
- if( bBigEndian ) SwapWord( 4, &nParts );
+ if( bBigEndian ) SwapWord( 4, &nPoints );
+ if( bBigEndian ) SwapWord( 4, &nParts );
+
+ if (nPoints < 0 || nParts < 0 ||
+ nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
+ hEntity, nPoints, nParts);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+
+ /* With the previous checks on nPoints and nParts, */
+ /* we should not overflow here and after */
+ /* since 50 M * (16 + 8 + 8) = 1 600 MB */
+ nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
+ if ( psShape->nSHPType == SHPT_POLYGONZ
+ || psShape->nSHPType == SHPT_ARCZ
+ || psShape->nSHPType == SHPT_MULTIPATCH )
+ {
+ nRequiredSize += 16 + 8 * nPoints;
+ }
+ if( psShape->nSHPType == SHPT_MULTIPATCH )
+ {
+ nRequiredSize += 4 * nParts;
+ }
+ if (nRequiredSize > nEntitySize)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.",
+ hEntity, nPoints, nParts, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
- psShape->nVertices = nPoints;
+ psShape->nVertices = nPoints;
psShape->padfX = (double *) calloc(nPoints,sizeof(double));
psShape->padfY = (double *) calloc(nPoints,sizeof(double));
psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
psShape->padfM = (double *) calloc(nPoints,sizeof(double));
- psShape->nParts = nParts;
+ psShape->nParts = nParts;
psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
psShape->panPartType = (int *) calloc(nParts,sizeof(int));
+
+ if (psShape->padfX == NULL ||
+ psShape->padfY == NULL ||
+ psShape->padfZ == NULL ||
+ psShape->padfM == NULL ||
+ psShape->panPartStart == NULL ||
+ psShape->panPartType == NULL)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. "
+ "Probably broken SHP file", hEntity, nPoints, nParts );
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
for( i = 0; i < nParts; i++ )
psShape->panPartType[i] = SHPP_RING;
/* -------------------------------------------------------------------- */
/* Copy out the part array from the record. */
/* -------------------------------------------------------------------- */
- memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
- for( i = 0; i < nParts; i++ )
- {
- if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
- }
+ memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
+ for( i = 0; i < nParts; i++ )
+ {
+ if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
+
+ /* We check that the offset is inside the vertex array */
+ if (psShape->panPartStart[i] < 0
+ || (psShape->panPartStart[i] >= psShape->nVertices
+ && psShape->nVertices > 0) )
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
+ hEntity, i, psShape->panPartStart[i], psShape->nVertices);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d",
+ hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ }
- nOffset = 44 + 8 + 4*nParts;
+ nOffset = 44 + 8 + 4*nParts;
/* -------------------------------------------------------------------- */
/* If this is a multipatch, we will also have parts types. */
/* -------------------------------------------------------------------- */
/* Copy out the vertices from the record. */
/* -------------------------------------------------------------------- */
- for( i = 0; i < nPoints; i++ )
- {
- memcpy(psShape->padfX + i,
- psSHP->pabyRec + nOffset + i * 16,
- 8 );
+ for( i = 0; i < nPoints; i++ )
+ {
+ memcpy(psShape->padfX + i,
+ psSHP->pabyRec + nOffset + i * 16,
+ 8 );
- memcpy(psShape->padfY + i,
- psSHP->pabyRec + nOffset + i * 16 + 8,
- 8 );
+ memcpy(psShape->padfY + i,
+ psSHP->pabyRec + nOffset + i * 16 + 8,
+ 8 );
- if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
- if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
- }
+ if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
+ if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
+ }
nOffset += 16*nPoints;
/* big enough, but really it will only occur for the Z shapes */
/* (options), and the M shapes. */
/* -------------------------------------------------------------------- */
- if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+ if( nEntitySize >= nOffset + 16 + 8*nPoints )
{
memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
psSHP->pabyRec + nOffset + 16 + i*8, 8 );
if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
}
+ psShape->bMeasureIsUsed = TRUE;
}
-
}
/* ==================================================================== */
|| psShape->nSHPType == SHPT_MULTIPOINTM
|| psShape->nSHPType == SHPT_MULTIPOINTZ )
{
- int32 nPoints;
- int i, nOffset;
+ int32_t nPoints;
+ int i, nOffset;
- memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
- if( bBigEndian ) SwapWord( 4, &nPoints );
+ if ( 44 + 4 > nEntitySize )
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
+
+ if( bBigEndian ) SwapWord( 4, &nPoints );
- psShape->nVertices = nPoints;
+ if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nPoints = %d",
+ hEntity, nPoints);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+
+ nRequiredSize = 48 + nPoints * 16;
+ if( psShape->nSHPType == SHPT_MULTIPOINTZ )
+ {
+ nRequiredSize += 16 + nPoints * 8;
+ }
+ if (nRequiredSize > nEntitySize)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
+ hEntity, nPoints, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+
+ psShape->nVertices = nPoints;
psShape->padfX = (double *) calloc(nPoints,sizeof(double));
psShape->padfY = (double *) calloc(nPoints,sizeof(double));
psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
psShape->padfM = (double *) calloc(nPoints,sizeof(double));
- for( i = 0; i < nPoints; i++ )
- {
- memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
- memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
+ if (psShape->padfX == NULL ||
+ psShape->padfY == NULL ||
+ psShape->padfZ == NULL ||
+ psShape->padfM == NULL)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. "
+ "Probably broken SHP file", hEntity, nPoints );
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
- if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
- if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
- }
+ for( i = 0; i < nPoints; i++ )
+ {
+ memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
+ memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
+
+ if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
+ if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
+ }
nOffset = 48 + 16*nPoints;
memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
- if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
+ if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
/* -------------------------------------------------------------------- */
/* If we have a Z coordinate, collect that now. */
/* big enough, but really it will only occur for the Z shapes */
/* (options), and the M shapes. */
/* -------------------------------------------------------------------- */
- if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+ if( nEntitySize >= nOffset + 16 + 8*nPoints )
{
memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
psSHP->pabyRec + nOffset + 16 + i*8, 8 );
if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
}
+ psShape->bMeasureIsUsed = TRUE;
}
}
{
int nOffset;
- psShape->nVertices = 1;
+ psShape->nVertices = 1;
psShape->padfX = (double *) calloc(1,sizeof(double));
psShape->padfY = (double *) calloc(1,sizeof(double));
psShape->padfZ = (double *) calloc(1,sizeof(double));
psShape->padfM = (double *) calloc(1,sizeof(double));
- memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
- memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
+ if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
+ memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
- if( bBigEndian ) SwapWord( 8, psShape->padfX );
- if( bBigEndian ) SwapWord( 8, psShape->padfY );
+ if( bBigEndian ) SwapWord( 8, psShape->padfX );
+ if( bBigEndian ) SwapWord( 8, psShape->padfY );
nOffset = 20 + 8;
/* big enough, but really it will only occur for the Z shapes */
/* (options), and the M shapes. */
/* -------------------------------------------------------------------- */
- if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 )
+ if( nEntitySize >= nOffset + 8 )
{
memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
if( bBigEndian ) SwapWord( 8, psShape->padfM );
+ psShape->bMeasureIsUsed = TRUE;
}
/* -------------------------------------------------------------------- */
&& psObject->nSHPType != SHPT_POLYGONM )
return 0;
+ if( psObject->nVertices == 0 || psObject->nParts == 0 )
+ return 0;
+
/* -------------------------------------------------------------------- */
/* Process each of the rings. */
/* -------------------------------------------------------------------- */
/* first ring is outer and all others are inner, but eventually */
/* we need to fix this to handle multiple island polygons and */
/* unordered sets of rings. */
+/* */
/* -------------------------------------------------------------------- */
- dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]];
- dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]];
+
+ /* Use point in the middle of segment to avoid testing
+ * common points of rings.
+ */
+ dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]]
+ + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2;
+ dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]]
+ + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2;
bInner = FALSE;
for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
else
iNext = 0;
- if( (psObject->padfY[iEdge+nVertStart] < dfTestY
- && psObject->padfY[iNext+nVertStart] >= dfTestY)
- || (psObject->padfY[iNext+nVertStart] < dfTestY
- && psObject->padfY[iEdge+nVertStart] >= dfTestY) )
+ /* Rule #1:
+ * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY)
+ * The rule #1 also excludes edges collinear with the ray.
+ */
+ if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY
+ && dfTestY <= psObject->padfY[iNext+nVertStart] )
+ || ( psObject->padfY[iNext+nVertStart] < dfTestY
+ && dfTestY <= psObject->padfY[iEdge+nVertStart] ) )
{
- if( psObject->padfX[iEdge+nVertStart]
- + (dfTestY - psObject->padfY[iEdge+nVertStart])
- / (psObject->padfY[iNext+nVertStart]
- - psObject->padfY[iEdge+nVertStart])
- * (psObject->padfX[iNext+nVertStart]
- - psObject->padfX[iEdge+nVertStart]) < dfTestX )
+ /* Rule #2:
+ * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
+ */
+ double const intersect =
+ ( psObject->padfX[iEdge+nVertStart]
+ + ( dfTestY - psObject->padfY[iEdge+nVertStart] )
+ / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] )
+ * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) );
+
+ if (intersect < dfTestX)
+ {
bInner = !bInner;
- }
+ }
+ }
}
- }
+ } /* for iCheckRing */
/* -------------------------------------------------------------------- */
/* Determine the current order of this ring so we will know if */
nVertCount = psObject->panPartStart[iOpRing+1]
- psObject->panPartStart[iOpRing];
- dfSum = 0.0;
- for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
+ if (nVertCount < 2)
+ continue;
+
+ dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]);
+ for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
{
- dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1]
- - psObject->padfY[iVert] * psObject->padfX[iVert+1];
+ dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]);
}
- dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart]
- - psObject->padfY[iVert] * psObject->padfX[nVertStart];
+ dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]);
/* -------------------------------------------------------------------- */
/* Reverse if necessary. */
return bAltered;
}
-#endif